1. 程式人生 > >BZOJ_1774_[Usaco2009 Dec]Toll 過路費_floyd

BZOJ_1774_[Usaco2009 Dec]Toll 過路費_floyd

存在 系列 路徑 有道 pri name span sort urn

BZOJ_1774_[Usaco2009 Dec]Toll 過路費_floyd

題意:

跟所有人一樣,農夫約翰以著寧教我負天下牛,休叫天下牛負我的偉大精神,日日夜夜苦思生 財之道。為了發財,他設置了一系列的規章制度,使得任何一只奶牛在農場中的道路行走,都 要向農夫約翰上交過路費。 農場中由N(1 <= N <= 250)片草地(標號為1到N),並且有M(1 <= M <= 10000)條 雙向道路連接草地A_j和B_j(1 <= A_j <= N; 1 <= B_j <= N)。

奶牛們從任意一片草 地出發可以抵達任意一片的草地。FJ已經在連接A_j和B_j的雙向道路上設置一個過路費L_j (1 <= L_j <= 100,000)。 可能有多條道路連接相同的兩片草地,但是不存在一條道路連接一片草地和這片草地本身。最 值得慶幸的是,奶牛從任意一篇草地出發,經過一系列的路徑,總是可以抵達其它的任意一片 草地。 除了貪得無厭,叫獸都不知道該說什麽好。

FJ竟然在每片草地上面也設置了一個過路費C_i (1 <= C_i <= 100000)。從一片草地到另外一片草地的費用,是經過的所有道路的過路 費之和,加上經過的所有的草地(包括起點和終點)的過路費的最大值。 任勞任怨的牛們希望去調查一下她們應該選擇那一條路徑。

她們要你寫一個程序,接受K(1 <= K <= 10,000)個問題並且輸出每個詢問對應的最小花費。第i個問題包含兩個數字s_i 和t_i(1 <= s_i <= N; 1 <= t_i <= N; s_i != t_i),表示起點和終點的草地。

分析:

這道題可以用來深入理解Floyd。首先我們開兩個數組記錄一下無點權和有點權時的最小花銷。

考慮暴力轉移時需要枚舉點來找到點權最大的點,如何優化這個枚舉?我們可以將點按點權從小往大排序,枚舉k,i,j從1到n實質上枚舉的是排序後的編號,這麽做的好處是轉移時點權最大的點一定在k,i,j三點之中,這樣就避免了枚舉點這一操作。

代碼:

 1 #include <stdio.h>
 2 #include <string.h>
 3 #include <algorithm>
 4 using namespace std;
 5 struct A
 6 {
 7     int p,v;
 8 }a[300];
 9 bool cmp(const A &x,const
A &y) 10 { 11 return x.v<y.v; 12 } 13 int f[300][300],n,m,q,g[300][300]; 14 int main() 15 { 16 scanf("%d%d%d",&n,&m,&q); 17 int x,y,z; 18 for(int i=1;i<=n;i++) 19 { 20 a[i].p=i; 21 scanf("%d",&a[i].v); 22 } 23 sort(a+1,a+n+1,cmp); 24 memset(f,0x3f,sizeof(f)); 25 memset(g,0x3f,sizeof(g)); 26 for(int i=1;i<=m;i++) 27 { 28 scanf("%d%d%d",&x,&y,&z); 29 f[x][y]=min(f[x][y],z); 30 f[y][x]=f[x][y]; 31 } 32 for(int k=1;k<=n;k++) 33 { 34 for(int i=1;i<=n;i++) 35 { 36 for(int j=1;j<=n;j++) 37 { 38 int i_=a[i].p,j_=a[j].p,k_=a[k].p; 39 f[i_][j_]=min(f[i_][j_],f[i_][k_]+f[k_][j_]); 40 g[i_][j_]=min(g[i_][j_],f[i_][j_]+max(max(a[i].v,a[j].v),a[k].v)); 41 } 42 } 43 } 44 for(int i=1;i<=q;i++) 45 { 46 scanf("%d%d",&x,&y); 47 printf("%d\n",g[x][y]); 48 } 49 }

BZOJ_1774_[Usaco2009 Dec]Toll 過路費_floyd