hdu4888 多校B 最大流以及最大流唯一判斷+輸出方案,hdu4888
題意,給一個矩陣,告訴你每行和、每列和,並且限制所填數不大於k,問矩陣是否唯一。
經典建圖不說了,第一次遇到判斷最大流唯一性的,學習了:用dfs來判斷殘網中是否還存在環,若存在,則表明繞這個環走一圈,(流一圈流量),還是最大流保持不變,說明還有解。輸出方案就EASY了。
WA了一天:第一TLE,因為這題卡DINIC,我的沒有最佳化,後來在zz1215學長加了一行代碼,在增廣的時候,若發現最小總流量已經為0,則標記該點層-1(不必要往下)。效果顯著。原因2:判斷環的時候,dfs判斷環寫錯有木有!不可原諒a!每次枚舉每個進入點(感覺可以最佳化),判斷環,遇到子孩子是祖先就證明有環。
#include<cstdio> //600ms#include<iostream>#include<queue>#include<cstring>#include<string>using namespace std;const int maxv=910;const int maxe=405*405*2+450;const int inf=0x3f3f3f3f;int n,m,k;int allsumn=0,allsumm=0;int nume=0;int e[maxe][4];int head[maxv];int nsum[405];int msum[405]; bool flag;void inline adde(int i,int j,int c){ e[nume][0]=j;e[nume][1]=head[i];head[i]=nume; e[nume++][2]=c; e[nume][0]=i;e[nume][1]=head[j];head[j]=nume; e[nume++][2]=0;}int lev[maxv];int vis[maxv];int ss=0;int tt=0;bool bfs(){ memset(lev,0,sizeof(lev)); memset(vis,0,sizeof(vis)); queue<int>q; q.push(ss); vis[ss]=1; while(!q.empty()) { int cur=q.front(); q.pop(); for(int i=head[cur];i!=-1;i=e[i][1]) { int v=e[i][0]; if(e[i][2]>0&&!vis[v]) { lev[v]=lev[cur]+1; // if(v==tt)return 1; //這句不加,速度更快 q.push(v); vis[v]=1; } } } return vis[tt];}int dfs(int u,int minf){ if(u==tt||minf==0)return minf; int sumf=0,f; for(int i=head[u];i!=-1&&minf;i=e[i][1]) { int v=e[i][0]; if(lev[v]==lev[u]+1&&e[i][2]>0) { f=dfs(v,e[i][2]<minf?e[i][2]:minf); sumf+=f; e[i][2]-=f;e[i^1][2]+=f; minf-=f; } } if(!sumf)lev[u]=-1; //關鍵最佳化! return sumf;}int dinic(){ int sum=0; while(bfs()) sum+=dfs(ss,inf); return sum;}void init(){ allsumm=allsumn=nume=0; memset(head,-1,sizeof(head)); ss=n+m;tt=n+m+1;}void read_build(){ for(int j=0;j<n;j++) { scanf("%d",&nsum[j]); allsumn+=nsum[j]; } for(int j=0;j<m;j++) { scanf("%d",&msum[j]); allsumm+=msum[j]; } for(int i=0;i<n;i++) for(int j=0;j<m;j++) { adde(i,j+n,k); } for(int i=0;i<n;i++) { adde(n+m,i,nsum[i]); } for(int i=0;i<m;i++) { adde(i+n,n+m+1,msum[i]); }}bool dfs_getother_ans(int u,int fa){ if(flag)return 1; for(int i=head[u];i!=-1;i=e[i][1]) { int v=e[i][0]; if(v==fa||e[i][2]<=0||v==n+m||v==n+m+1)continue; if(!vis[v]) { vis[v]=1; dfs_getother_ans(v,u); vis[v]=0; //出來的時候標記回來啊! } else { flag=1; return 1; } if(flag)return 1; } return 0;}int ansjz[405][405];int main(){ while(scanf("%d%d%d",&n,&m,&k)!=EOF) { init(); read_build(); if(allsumm!=allsumn) {printf("Impossible\n");continue;} int ans=dinic(); if(ans!=allsumm) { printf("Impossible\n"); } else { flag=0; for(int i=0;i<n;i++) { vis[i]=1; if(dfs_getother_ans(i,-1)) { flag=1; break; } vis[i]=0; } if(flag) printf("Not Unique\n"); else { printf("Unique\n"); for(int i=0;i<=n-1;i++) for(int j=head[i];j!=-1;j=e[j][1]) { if(j%2==0) { ansjz[i][e[j][0]-n]=k-e[j][2]; } } for(int i=0;i<n;i++) for(int j=0;j<m;j++) if(j==m-1)printf("%d\n",ansjz[i][j]); else printf("%d ",ansjz[i][j]); } } } return 0;}
急 數學建模最大流問題——運輸網路
<第一歩 建立限制式>
對於每一條線段的流量,建立出限制式,如:
S_a <= 26
S_b <= 12
a_b <= 5
....
<第二歩先利用流量瓶頸將限制式轉化為等號>
(1)將 c_f, d_f, d_g, e_g 切斷,則S 將無法連通 M1, M2, M3 所以 c_f, d_f, d_g, e_g 為溝通左右的橋樑組合之一.
(2)已知全部需要傳遞的總噸數為 10 + 8 + 8 = 26 ,其中 10 噸給M1,8 噸給M2,8 噸給M3
(3)又 c_f, d_f, d_g, e_g 四個流量的總和為 10 + 6 + 5 + 5 = 26 故 c_f, d_f, d_g, e_g 四個流量都要用完,因此可以寫成下列四個等式:
(4) c_f = 10, d_f = 6, d_g = 5, e_g = 5
<第三歩 切割問題成兩部分>
(1)依據 c_f=10 且 d_f=6 ,得知 f 點會獲得 16 噸貨物
依據 d_g=5 且 e_g=5 ,得知 g 點會獲得 10 噸貨物
依此可以建立<子問題A>,即:
----------------------------------
如何將 f 點的16 噸與 g 點的10 噸,分送給M1, M2, M3 且量為10, 8, 8
----------------------------------
(2)依據 c_f=10 ,得知 c 點會獲得 10 噸貨物
依據 d_f=6 且 d_g=5 ,得知 d 點會獲得 11 噸貨物
依據 e_g=5 ,得知 e 點會獲得 5 噸貨物
依此可以建立<子問題B>,即:
----------------------------------
如何將 S 點的東西,分送到 c, d, e 且量為10, 11, 5
----------------------------------
<第四歩 解決子問題A>
(1)g 點的10 噸貨物只能傳向M3 ,M3 領收8 噸之後,剩下的2 噸只能傳給M2 .傳遞完畢之後,g_M3 之間的流量還剩 15-10=5 ,M3_M2 之間的流量還剩 5-2=3
(2)M1 只能靠 f 點提供貨物,所以 M1 所需的10 噸必須完全通過 f_M1 之間的連線
(3)最後 f 還剩下6 噸貨物要傳給 M2 ,這可以完全通過上方的 f_M1 連線完成;也可以通過下方的 f_g 連線,分流 3 噸以下的物資過去(別忘了M3_M2 之間只剩下3 噸的餘額).
承上,<子問題A>獲得解決.
<第五歩 解決子問題B>
(1)c 點的 10 噸貨物必須來自於a ,故 a_c 的流量還剩下 20-10=10
(2)e 點的 5 噸貨物必須來自於b ,故 b_e 的流量還剩下 6-5=1
(3)d 點的 11 噸貨物可能來自於 c, b, e .
(3-1)由於 c_d 的流量限制,c 點最多隻能提供 d 點5 噸
(3-2)由於 b_d 的流量限制,b 點最多隻能提供 d 點5 噸
(3-3)由於 b_e 的流量限制(參考上述第2點),e 點最多隻能提供 d 點1 噸
(3-4)所以 d 點的貨物來源是唯一的:b 與c 各給5 噸,e 給1 噸
(4)結合第1點與第3-1點,a 點全部需要提供 10+5 = 15 噸
(5)結合第2點、第3-2點、第3-3點,b 點全部需要提供 5+5+1 = 11......餘下全文>>
最大流問題建模
<第一歩 建立限制式>
對於每一條線段的流量,建立出限制式,如:
S_a <= 26
S_b <= 12
a_b <= 5
....
<第二歩先利用流量瓶頸將限制式轉化為等號>
(1)將 c_f, d_f, d_g, e_g 切斷,則S 將無法連通 M1, M2, M3 所以 c_f, d_f, d_g, e_g 為溝通左右的橋樑組合之一.
(2)已知全部需要傳遞的總噸數為 10 + 8 + 8 = 26 ,其中 10 噸給M1,8 噸給M2,8 噸給M3
(3)又 c_f, d_f, d_g, e_g 四個流量的總和為 10 + 6 + 5 + 5 = 26 故 c_f, d_f, d_g, e_g 四個流量都要用完,因此可以寫成下列四個等式:
(4) c_f = 10, d_f = 6, d_g = 5, e_g = 5
<第三歩 切割問題成兩部分>
(1)依據 c_f=10 且 d_f=6 ,得知 f 點會獲得 16 噸貨物
依據 d_g=5 且 e_g=5 ,得知 g 點會獲得 10 噸貨物
依此可以建立<子問題A>,即:
----------------------------------
如何將 f 點的16 噸與 g 點的10 噸,分送給M1, M2, M3 且量為10, 8, 8
----------------------------------
(2)依據 c_f=10 ,得知 c 點會獲得 10 噸貨物
依據 d_f=6 且 d_g=5 ,得知 d 點會獲得 11 噸貨物
依據 e_g=5 ,得知 e 點會獲得 5 噸貨物
依此可以建立<子問題B>,即:
----------------------------------
如何將 S 點的東西,分送到 c, d, e 且量為10, 11, 5
----------------------------------
<第四歩 解決子問題A>
(1)g 點的10 噸貨物只能傳向M3 ,M3 領收8 噸之後,剩下的2 噸只能傳給M2 .傳遞完畢之後,g_M3 之間的流量還剩 15-10=5 ,M3_M2 之間的流量還剩 5-2=3
(2)M1 只能靠 f 點提供貨物,所以 M1 所需的10 噸必須完全通過 f_M1 之間的連線
(3)最後 f 還剩下6 噸貨物要傳給 M2 ,這可以完全通過上方的 f_M1 連線完成;也可以通過下方的 f_g 連線,分流 3 噸以下的物資過去(別忘了M3_M2 之間只剩下3 噸的餘額).
承上,<子問題A>獲得解決.
<第五歩 解決子問題B>
(1)c 點的 10 噸貨物必須來自於a ,故 a_c 的流量還剩下 20-10=10
(2)e 點的 5 噸貨物必須來自於b ,故 b_e 的流量還剩下 6-5=1
(3)d 點的 11 噸貨物可能來自於 c, b, e .
(3-1)由於 c_d 的流量限制,c 點最多隻能提供 d 點5 噸
(3-2)由於 b_d 的流量限制,b 點最多隻能提供 d 點5 噸
(3-3)由於 b_e 的流量限制(參考上述第2點),e 點最多隻能提供 d 點1 噸
(3-4)所以 d 點的貨物來源是唯一的:b 與c 各給5 噸,e 給1 噸
(4)結合第1點與第3-1點,a 點全部需要提供 10+5 = 15 噸
(5)結合第2點、第3-2點、第3-3點,b 點全部需要提供 5+5+1 = 11......餘下全文>>