題目描述
輸入格式
輸出格式
樣例
題解
無向圖的最小環問題啊,不知道在多少場考試中間卡住了我的臨門一腳。——hzz
教材:http://www.cnblogs.com/Yz81128/archive/2012/08/15/2640940.html#undefined
這個最小環中,一定會有一個編號最大的點。
那么我們設$dp[i][j][k]$為在只經過$1$~$k$的點時,$i$,$j$的最短距離。
那么如果我們設最小環中最大的點為$k$,它兩邊有點$i$,$j$,
那么答案的最小值就是$dp[i][j][k-1]+g[j][k]+g[k][i]$。
//
然后我們聯想Floyd的過程,
$for$ $int$ $k=1$ $to$ $n$
$for$ $int$ $i=1$ $to$ $n$
$for$ $int$ $j=1$ $to$ $n$
也就是說,在$k$時,$i$,$j$的最小值都只中轉了$1$~$k$的點。
這不就是我們要的$dp[i][j][k]$嘛?!
於是我們可以在Floyd的同時順便維護答案。
並且$dp$數組的$k$那一維還是可以省掉的。
//詳見代碼吧qwq
1 編號 題目 狀態 分數 總時間 內存 代碼 / 答案文件 提交者 提交時間 2 #239580 #10072. 「一本通 3.2 例 1」Sightseeing Trip Accepted 100 45 ms 380 KiB C++ / 1.2 K qwerta 2018-10-22 16:48:53 3 4 #include<iostream> 5 #include<cstring> 6 #include<cstdio> 7 #include<cmath> 8 using namespace std; 9 #define LL long long 10 int g[103][103];//直接連邊的距離 11 int dis[103][103];//有中轉點的距離 12 int q[103]; 13 int toq=0; 14 int zz[103][103];//zz[i][j]表示從i到j的最小路徑的中轉點 15 void getpath(int u,int v) 16 { 17 if(!zz[u][v])return; 18 getpath(u,zz[u][v]);//從u往中轉點找 19 q[++toq]=zz[u][v]; 20 getpath(zz[u][v],v); 21 return; 22 } 23 int main() 24 { 25 //freopen("a.in","r",stdin); 26 int n,m; 27 scanf("%d%d",&n,&m); 28 memset(g,127,sizeof(g)); 29 for(int i=1;i<=n;++i) 30 g[i][i]=0; 31 for(int i=1;i<=m;++i) 32 { 33 int x,y,l; 34 scanf("%d%d%d",&x,&y,&l); 35 g[x][y]=min(g[x][y],l); 36 g[y][x]=g[x][y]; 37 } 38 memcpy(dis,g,sizeof(g)); 39 long long ans=1e9+7; 40 for(int k=1;k<=n;++k) 41 { 42 for(int i=1;i<k;++i) 43 for(int j=i+1;j<k;++j)//因為k為環內最大點,所以只能循環到k-1 44 { 45 if(1LL*dis[i][j]+g[j][k]+g[k][i]<ans) 46 { 47 ans=dis[i][j]+g[j][k]+g[k][i]; 48 toq=0; 49 q[++toq]=i; 50 getpath(i,j); 51 q[++toq]=j; 52 q[++toq]=k; 53 } 54 } 55 //然后是正常Floyd 56 for(int i=1;i<=n;++i) 57 for(int j=1;j<=n;++j) 58 { 59 if(1LL*dis[i][k]+dis[k][j]<dis[i][j]) 60 { 61 dis[i][j]=dis[i][k]+dis[k][j]; 62 zz[i][j]=k; 63 } 64 } 65 } 66 if(!toq){cout<<"No solution.";return 0;} 67 for(int i=1;i<=toq;++i) 68 cout<<q[i]<<" "; 69 return 0; 70 }
