【試題來源】
2011中國國家集訓隊命題答辯
【問題描述】
高一一班的座位表是個n*m的矩陣,經過一個學期的相處,每個同學和前后左右相鄰的同學互相成為了好朋友。這學期要分文理科了,每個同學對于選擇文科與理科有著自己的喜悅值,而一對好朋友如果能同時選文科或者理科,那么他們又將收獲一些喜悅值。作為計算機競賽教練的scp大老板,想知道如何分配可以使得全班的喜悅值總和最大。
【輸入格式】
第一行兩個正整數n,m。
接下來是六個矩陣
第一個矩陣為n行m列 此矩陣的第i行第j列的數字表示座位在第i行第j列的同學選擇文科獲得的喜悅值。
第二個矩陣為n行m列 此矩陣的第i行第j列的數字表示座位在第i行第j列的同學選擇理科獲得的喜悅值。
第三個矩陣為n-1行m列 此矩陣的第i行第j列的數字表示座位在第i行第j列的同學與第i+1行第j列的同學同時選擇文科獲得的額外喜悅值。
第四個矩陣為n-1行m列 此矩陣的第i行第j列的數字表示座位在第i行第j列的同學與第i+1行第j列的同學同時選擇理科獲得的額外喜悅值。
第五個矩陣為n行m-1列 此矩陣的第i行第j列的數字表示座位在第i行第j列的同學與第i行第j+1列的同學同時選擇文科獲得的額外喜悅值。
第六個矩陣為n行m-1列 此矩陣的第i行第j列的數字表示座位在第i行第j列的同學與第i行第j+1列的同學同時選擇理科獲得的額外喜悅值。
接下來是六個矩陣
第一個矩陣為n行m列 此矩陣的第i行第j列的數字表示座位在第i行第j列的同學選擇文科獲得的喜悅值。
第二個矩陣為n行m列 此矩陣的第i行第j列的數字表示座位在第i行第j列的同學選擇理科獲得的喜悅值。
第三個矩陣為n-1行m列 此矩陣的第i行第j列的數字表示座位在第i行第j列的同學與第i+1行第j列的同學同時選擇文科獲得的額外喜悅值。
第四個矩陣為n-1行m列 此矩陣的第i行第j列的數字表示座位在第i行第j列的同學與第i+1行第j列的同學同時選擇理科獲得的額外喜悅值。
第五個矩陣為n行m-1列 此矩陣的第i行第j列的數字表示座位在第i行第j列的同學與第i行第j+1列的同學同時選擇文科獲得的額外喜悅值。
第六個矩陣為n行m-1列 此矩陣的第i行第j列的數字表示座位在第i行第j列的同學與第i行第j+1列的同學同時選擇理科獲得的額外喜悅值。
【輸出格式】
輸出一個整數,表示喜悅值總和的最大值
【樣例輸入】
1 2
1 1
100 110
1
1000
1 1
100 110
1
1000
【樣例輸出】
1210
【樣例說明】
兩人都選理,則獲得100+110+1000的喜悅值。
【數據規模和約定】
對于10%以內的數據,n,m<=4
對于30%以內的數據,n,m<=8
對于100%以內的數據,n,m<=100 數據保證答案在2^30以內
對于100%的數據,時間限制為0.5s。
對于30%以內的數據,n,m<=8
對于100%以內的數據,n,m<=100 數據保證答案在2^30以內
對于100%的數據,時間限制為0.5s。
【題解】
? ??這道題的難點在于確定邊權。最小割問題,割去的就是我們失去的部分;兩點之間有關系,總是通過建邊來實現的。對于這道題來說,每種情況我們都失去了什么?以源點代表文科,匯點代表理科。都選一科(三角環),失去了共同選另一科和分別選另一科的喜悅值。分別選兩科(二字形),失去了兩個共同喜悅值和兩個單獨選另一科的喜悅值。(可以證明可能出現的情況只有這兩種,否則都不會是最小割)相同位置的邊權構成一定相同,因此用數學方法推出每個人到源點或匯點的邊權為個人喜悅值+1/2共同喜悅值,兩點之間邊權為1/2都選文+1/2都選理。注意共同邊要雙向建邊,因為兩點之間是完全等效的;每個人向源點和匯點的邊應該在邊權全部處理完之后再統一添加。
? ? 可以發現邊權會出現實型,結果卻一定是整型。對于這種情況,可以把邊權全部*2,最后結果再/2來避免double的麻煩。dfs函數中有一個語句:if(!f) break;原先從來沒打過,這道題不加這個卻會超時,加了之后直接上榜,確實是一個非常有理有據的優化。


#include<iostream> #include<cstdio> #include<cstring> #include<queue> using namespace std; const int sj=105; int n,m,sx[sj][sj],e,h[sj*sj],s,t,dep[sj*sj]; int w[sj][sj],l[sj][sj],g[sj][sj],z[sj][sj],a1,ans; struct B {int ne,v,w; }b[sj*sj*10]; queue<int> q; void add(int x,int y,int z) {b[e].v=y;b[e].w=z;b[e].ne=h[x];h[x]=e++; } void init() {scanf("%d%d",&n,&m);t=n*m+1;memset(h,-1,sizeof(h));for(int i=1;i<=n;i++)for(int j=1;j<=m;j++){scanf("%d",&w[i][j]);sx[i][j]=(i-1)*m+j;ans+=w[i][j];w[i][j]*=2;}for(int i=1;i<=n;i++)for(int j=1;j<=m;j++){scanf("%d",&l[i][j]);ans+=l[i][j];l[i][j]*=2;}for(int i=1;i<n;i++)for(int j=1;j<=m;j++){scanf("%d",&a1);ans+=a1;g[i][j]=a1;w[i][j]+=a1;w[i+1][j]+=a1;}for(int i=1;i<n;i++)for(int j=1;j<=m;j++){scanf("%d",&a1);ans+=a1;g[i][j]+=a1;l[i][j]+=a1;l[i+1][j]+=a1;add(sx[i][j],sx[i+1][j],g[i][j]);add(sx[i+1][j],sx[i][j],g[i][j]);}for(int i=1;i<=n;i++)for(int j=1;j<m;j++){scanf("%d",&a1);ans+=a1;z[i][j]=a1;w[i][j]+=a1;w[i][j+1]+=a1;add(s,sx[i][j],w[i][j]);add(sx[i][j],s,0);}for(int i=1;i<=n;i++){add(s,sx[i][m],w[i][m]);add(sx[i][m],s,0);}for(int i=1;i<=n;i++)for(int j=1;j<m;j++){scanf("%d",&a1);ans+=a1;z[i][j]+=a1;l[i][j]+=a1;l[i][j+1]+=a1;add(t,sx[i][j],0);add(sx[i][j],t,l[i][j]);add(sx[i][j],sx[i][j+1],z[i][j]);add(sx[i][j+1],sx[i][j],z[i][j]);}for(int i=1;i<=n;i++){add(sx[i][m],t,l[i][m]);add(t,sx[i][m],0);}ans*=2; } bool bfs(int x) {while(!q.empty()) q.pop();memset(dep,0,sizeof(dep));dep[x]=1;q.push(x);while(!q.empty()){x=q.front();q.pop();for(int i=h[x];i!=-1;i=b[i].ne)if(!dep[b[i].v]&&b[i].w){dep[b[i].v]=dep[x]+1;if(b[i].v==t) return 1;q.push(b[i].v);}}return 0; } int bj(int x,int y) {return x<y?x:y; } int dfs(int x,int f) {if(x==t) return f;int ans=0,d;for(int i=h[x];i!=-1;i=b[i].ne)if(dep[b[i].v]==dep[x]+1&&b[i].w){d=dfs(b[i].v,bj(f,b[i].w));f-=d;ans+=d;b[i].w-=d;b[i^1].w+=d;if(!f) break;}if(!ans) dep[x]=-1;return ans; } int main() {init();while(bfs(s)) ans-=dfs(s,0x7fffffff);printf("%d",ans/2);return 0; }
?