P1462 通往奥格瑞玛的道路[最短路+二分+堆优化]
題目來源:洛谷
題目背景
在艾澤拉斯大陸上有一位名叫歪嘴哦的神奇術士,他是部落的中堅力量
有一天他醒來后發現自己居然到了聯盟的主城暴風城
在被眾多聯盟的士兵攻擊后,他決定逃回自己的家鄉奧格瑞瑪
題目描述
在艾澤拉斯,有n個城市。編號為1,2,3,...,n。
城市之間有m條雙向的公路,連接著兩個城市,從某個城市到另一個城市,會遭到聯盟的攻擊,進而損失一定的血量。
每次經過一個城市,都會被收取一定的過路費(包括起點和終點)。路上并沒有收費站。
假設1為暴風城,n為奧格瑞瑪,而他的血量最多為b,出發時他的血量是滿的。
歪嘴哦不希望花很多錢,他想知道,在可以到達奧格瑞瑪的情況下,他所經過的所有城市中最多的一次收取的費用的最小值是多少。
輸入輸出格式
輸入格式:
?
第一行3個正整數,n,m,b。分別表示有n個城市,m條公路,歪嘴哦的血量為b。
接下來有n行,每行1個正整數,fi。表示經過城市i,需要交費fi元。
再接下來有m行,每行3個正整數,ai,bi,ci(1<=ai,bi<=n)。表示城市ai和城市bi之間有一條公路,如果從城市ai到城市bi,或者從城市bi到城市ai,會損失ci的血量。
?
輸出格式:
?
僅一個整數,表示歪嘴哦交費最多的一次的最小值。
如果他無法到達奧格瑞瑪,輸出AFK。
?
輸入輸出樣例
輸入樣例#1:?4 4 8 8 5 6 10 2 1 2 2 4 1 1 3 4 3 4 3 輸出樣例#1:?
10
說明
對于60%的數據,滿足n≤200,m≤10000,b≤200
對于100%的數據,滿足n≤10000,m≤50000,b≤1000000000
對于100%的數據,滿足ci≤1000000000,fi≤1000000000,可能有兩條邊連接著相同的城市。(注意細節)
?
這道題值得一寫啊。。。我卡了差不多十次。每次都是一點點瑕疵。
?
一開始想著dfs暴力搞定,如你所料,TLE了。。。于是轉向求助于dijkstra,看了看書發現最短路可以做這道題,結果是最后騙AFK騙到45分。。。
無奈求助于題解,遂用二分解之。
?
我解釋的挺不清楚的,因為我自己也沒有理解透徹,這道題比較繞,容易把最大跟最小還有限制條件一下子搞混了,就全亂了。
?
解析:
?
題目有點難理解,先解釋一下題目:
對于一個有向圖G,有兩種信息:一個是每條邊的權,一個是每個點的費用。
對于這個給定的有向圖G,從1到n的通路是一定的。
限制條件:給定一個值,使得某條1到n的通路的邊權之和不大于這個值。
我們假設滿足以上限制的這些1到n的通路的集合為S(n)。
對于這個集合S中的每個元素(即每條通路),我們記該路徑上點的費用的最大值為另一個集合max(n)。
?
嘿,有了上面這些數學語言的描述作基礎,我們可以很簡單的理解題意。
我們就是要求出max集合中的最小值。
?
思路大概是這樣:
我們首先要得到合法的可達的通路,使得歪嘴哦不死,然后就是對這條通路做限制,使得這條通路對應的max盡可能小。
我們知道,可選的點是一定的,所以本題的答案一定在所有點的集合中,為了得到更小的讓歪嘴哦不死的一個max,我們有一個解法。
假設所有可選的點是a1,a2,...an,我們就把它們排個序,從大到小來找一個既使得歪嘴哦不死,又使得max盡量小的點費用。
因為要讓他盡可能不死,所以我們用最短路算法來盡量讓他活下來。
?
哎,發現了沒有,“從大到小來找一個既使得歪嘴哦不死,又使得max盡量小的點費用。”這樣一個設定,使得我們可以用二分去解它!
二分的前提是,歪嘴哦不死!
?
我用了一個dij來判斷歪嘴哦在當前點費用限制下會不會死:
1 bool dijkstra(int limit) 2 { 3 memset(v,0,sizeof(v)); 4 memset(d,0x3f,sizeof(d)); 5 d[1]=0; 6 q.push(make_pair(0,1)); 7 while(q.size()){ 8 int index=q.top().second;q.pop(); 9 if(v[index]) continue; 10 v[index]=1; 11 for(int i=head[index];i;i=g[i].next){ 12 int y=g[i].ver,z=g[i].edge; 13 if(d[y]>d[index]+z&&c[y]<=limit){ 14 d[y]=d[index]+z; 15 q.push(make_pair(d[y],y)); 16 } 17 } 18 } 19 if(d[n]<=hp) return 1; 20 else return 0; 21 }這個limit就是我們對這條最短路能走過的點費用最大值的限制。
?
然后是這個二分:
1 sort(u+1,u+n+1); 2 int l=1,r=n,ans=c[n]; 3 while(l<=r){ 4 int mid=(l+r)>>1; 5 if(dijkstra(u[mid])) 6 { 7 r=mid-1;ans=u[mid]; 8 } 9 else l=mid+1; 10 } 11 printf("%d\n",ans);不做任何關于點費用的限制歪嘴哦都會死的話,那就只能AFK了(他真慘):
1 if(!dijkstra(INF)){ 2 printf("AFK\n");return 0; 3 }?
參考代碼:
具體的我就不寫注釋了,上面已經寫的很清楚了。
#include<cstdio> #include<iostream> #include<algorithm> #include<queue> #include<vector> #include<cstring> #define N 10010 #define INF 0x3fffffff using namespace std; int head[N],d[N*100],tot,n,m,hp; int u[N*100],c[N*100]; bool v[N*100]; priority_queue<pair<int,int>,vector<pair<int,int> >,greater<pair<int,int> > > q; struct node{int ver,next,edge; }g[N*100]; void add(int x,int y,int val) {g[++tot].ver=y,g[tot].edge=val;g[tot].next=head[x],head[x]=tot; }bool dijkstra(int limit) {memset(v,0,sizeof(v));memset(d,0x3f,sizeof(d));d[1]=0;q.push(make_pair(0,1));while(q.size()){int index=q.top().second;q.pop();if(v[index]) continue;v[index]=1;for(int i=head[index];i;i=g[i].next){int y=g[i].ver,z=g[i].edge;if(d[y]>d[index]+z&&c[y]<=limit){d[y]=d[index]+z;q.push(make_pair(d[y],y));}}}if(d[n]<=hp) return 1;else return 0; } int main() {scanf("%d%d%d",&n,&m,&hp);for(int i=1;i<=n;i++){scanf("%d",&c[i]);//到達每個點的費用 u[i]=c[i];//我們額外搞一個數組來二分 }for(int i=1;i<=m;i++){int x,y,val;scanf("%d%d%d",&x,&y,&val);//過邊損失血量 if(x==y) continue;add(x,y,val);add(y,x,val);}if(!dijkstra(INF)){printf("AFK\n");return 0;}sort(u+1,u+n+1);int l=1,r=n,ans=c[n];while(l<=r){int mid=(l+r)>>1;if(dijkstra(u[mid])){r=mid-1;ans=u[mid];}else l=mid+1;}printf("%d\n",ans);return 0; }2019-05-31?19:07:02
轉載于:https://www.cnblogs.com/DarkValkyrie/p/10951142.html
總結
以上是生活随笔為你收集整理的P1462 通往奥格瑞玛的道路[最短路+二分+堆优化]的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Java通过JDBC连接SQL Serv
- 下一篇: php 常用字符串函数