E.漢米頓的麻煩
題目連結
他題目時不時提到漢米頓,是多愛漢米頓XDD(這一題跟漢米頓根本沒關係)
題目大意 在一張n(n≤100)個點的圖中,尋找路徑長度最短的環
那就用Floyd-Warshall 找最小的環就好!
Floyd-Warshall 這就是dp的作法,用 $O(N^3)$ 的時間進行轉移,就能得到全點對的最短路徑。這邊有一個重要的轉移順序,也就是中點-起點-終點進行轉移,如果把dp展開就會發現中點必須在最外層進行轉移:
定義 $dp[k][i][j]$ 為點 $i$ 走到點 $j$ ,只能經過前k個點的最短路徑,則轉移式變成:
每一個k+1都是由k轉移而來,仰賴k的點的配對,因此必須最先轉移中點k的部分!(不過順序顛倒也不會怎樣啦,上面「課程內容」的地方有寫到)
同樣這一份code也可以判斷有沒有負環,只要ans小於0的話代表有負環(有的話距離是不能用的喔,因為Floyd-Warshall不能處理負環,但負邊是可以處理的)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 #include <bits/stdc++.h> #define ll long long #define int long long #define Orz ios::sync_with_stdio(0),cin.tie(0) #define N 101 #define FOR(i,n) for(int i=0;i<n;i++) #define pii pair<int,int> #define pid pair<int,double> #define pdi pair<double,int> using namespace std;int n,dp[N][N];void solve () { memset (dp,0x3f3f3f3f ,sizeof (dp)); for (int i=0 ;i<n;i++){ for (int j=0 ;j<n;j++){ int temp;cin>>temp; if (temp!=0 )dp[i][j] = temp; } } for (int k=0 ;k<n;k++){ for (int i=0 ;i<n;i++){ for (int j=0 ;j<n;j++){ dp[i][j] = min (dp[i][j],dp[i][k]+dp[k][j]); } } } int ans = INT_MAX; for (int i=0 ;i<n;i++){ ans = min (ans,dp[i][i]); } if (ans == INT_MAX)cout<<-1 <<endl; else cout<<ans<<endl; } signed main () { while (cin>>n && n!=0 ){ solve (); } }