1. 程式人生 > >HDU 4370 0 or 1(spfa+思維建圖+計算最小環)

HDU 4370 0 or 1(spfa+思維建圖+計算最小環)

inf 計算 最小 star while arch mes targe space

題目鏈接:http://acm.hdu.edu.cn/showproblem.php?pid=4370

題目大意:有一個n*n的矩陣Cij(1<=i,j<=n),要找到矩陣Xij(i<=1,j<=n)滿足以下條件:

     1.X 12+X 13+...X 1n=1
     2.X 1n+X 2n+...X n-1n=1
     3.for each i (1<i<n), satisfies ∑X ki (1<=k<=n)=∑X ij (1<=j<=n).

舉個例子, 如果 n=4,上面條件等價於以下情況:
     X 12

+X 13+X 14=1
     X 14+X 24+X 34=1
     X 12+X 22+X 32+X 42=X 21+X 22+X 23+X 24
     X 13+X 23+X 33+X 43=X 31+X 32+X 33+X 34

找出 ∑C ij*X ij(1<=i,j<=n)的最小值。

解題思路:這題主要是考建圖的思維,看了kuangbin寫的才會,下面都是照搬他博客的東西。解題的關鍵在於如何看出這個模型的本質。


3個條件明顯在刻畫未知數之間的關系,從圖論的角度思考問題,容易得到下面3個結論:

     ①.X12+X13+...X1n=1 於是1號節點的出度為1


     ②..X1n+X2n+...Xn-1n=1 於是n號節點的入度為1

     ③.∑Xki =∑Xij 於是2~n-1號節點的入度必須等於出度

於是3個條件等價於一條從1號節點到n號節點的路徑,故Xij=1表示需要經過邊(i,j),代價為Cij。Xij=0表示不經過邊(i,j)。註意到Cij非負且題目要求總代價最小,因此最優答案的路徑一定可以對應一條簡單路徑。

最終,我們直接讀入邊權的鄰接矩陣,跑一次1到n的最短路即可,記最短路為path。

以上情況設為A

還有如下的情況B:

從1出發,走一個環(至少經過1個點,即不能是自環),回到1;從n出發,走一個環(同理),回到n。

容易驗證,這是符合題目條件的。且A || B為該題要求的充要條件。

由於邊權非負,於是兩個環對應著兩個簡單環。

因此我們可以從1出發,找一個最小花費環,記代價為c1,再從n出發,找一個最小花費環,記代價為c2。

故最終答案為min(spfa(1,n),spfa(1,1)+spfa(n,n))。

spfa裏面計算最小環,還需要一點修改,當計算環時(dis[start]==INF且start不入隊,計算所有start能到達的點並入隊)。

代碼:

 1 #include<iostream>
 2 #include<cstring>
 3 #include<algorithm>
 4 #include<queue>
 5 #include<cstdio>
 6 using namespace std;
 7 const int N=3e2+5;
 8 const int INF=0x3f3f3f3f;
 9 
10 int n;
11 int cost[N][N],dis[N];
12 bool vis[N];
13 
14 int spfa(int s,int e){
15     memset(dis,0x3f,sizeof(dis));
16     memset(vis,false,sizeof(vis));
17     queue<int>q;
18     if(s==e){
19         for(int i=1;i<=n;i++){
20             if(i!=s){
21                 q.push(i);
22                 dis[i]=cost[s][i];
23                 vis[i]=true;
24             }
25         }
26     }
27     else{
28         dis[s]=0;
29         q.push(s);
30     }
31     while(!q.empty()){
32         int k=q.front();
33         q.pop();
34         vis[k]=false;
35         for(int i=1;i<=n;i++){
36             if(dis[k]+cost[k][i]<dis[i]){
37                 dis[i]=dis[k]+cost[k][i];
38                 if(!vis[i]){
39                     q.push(i);
40                     vis[i]=true;
41                 }
42             }
43         }
44     }
45     return dis[e];
46 }
47 
48 int main(){
49     while(scanf("%d",&n)!=EOF){
50         for(int i=1;i<=n;i++){
51             for(int j=1;j<=n;j++){
52                 scanf("%d",&cost[i][j]);
53             }
54         }
55         int ans=min(spfa(1,n),spfa(1,1)+spfa(n,n));
56         printf("%d\n",ans);
57     }
58     return 0;
59 }

HDU 4370 0 or 1(spfa+思維建圖+計算最小環)