轉載請註明出處,謝謝http://blog.csdn.net/acm_cxlove/article/details/7854526
by---cxlove
題目:一個只含0,1的矩陣
1.X12+X13+...X1n=1
2.X1n+X2n+...Xn-1n=1
3.for each i (1<i<n), satisfies ∑Xki (1<=k<=n)=∑Xij (1<=j<=n).
另外一個矩陣,兩個矩陣相乘最小
http://acm.hdu.edu.cn/showproblem.php?pid=4370
顯然,題目給的是一個0/1規劃模型。
解題的關鍵在於如何看出這個模型的本質。
3個條件明顯在刻畫未知數之間的關係,從圖論的角度思考問題,容易得到下面3個結論:
1.X12+X13+...X1n=1 於是1號節點的出度為1
2..X1n+X2n+...Xn-1n=1 於是n號節點的入度為1
3.∑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。(只需在最短路演算法更新權值時多加一條記錄即可:if(i==S) cir=min(cir,dis[u]+g[u][i]))
故最終答案為min(path,c1+c2)
#include<iostream>#include<cstdio>#include<map>#include<cstring>#include<cmath>#include<vector>#include<algorithm>#include<set>#include<queue>#define inf 1<<27#define N 105#define Min(a,b) ((a)<(b)?(a):(b))#define Max(a,b) ((a)>(b)?(a):(b))#define pb(a) push_back(a)#define LL long longusing namespace std;int n,path[305][305];int dist[305],vis[305];void Spfa(int s){ queue<int>que; memset(vis,0,sizeof(vis)); for(int i=0;i<n;i++){ if(i==s) dist[i]=inf; else{ dist[i]=path[s][i]; que.push(i); vis[i]=1; } } while(!que.empty()){ int u=que.front(); que.pop(); vis[u]=0; for(int i=0;i<n;i++){ if(dist[i]>dist[u]+path[u][i]){ dist[i]=dist[u]+path[u][i]; if(!vis[i]){ vis[i]=1; que.push(i); } } } }}int main(){ while(scanf("%d",&n)!=EOF){ for(int i=0;i<n;i++) for(int j=0;j<n;j++) scanf("%d",&path[i][j]); int ans,c1,c2; Spfa(0); ans=dist[n-1]; c1=dist[0]; Spfa(n-1); c2=dist[n-1]; printf("%d\n",min(ans,c1+c2)); } return 0;}