標籤:pos color 執行個體化 inpu += 代碼 tput main 表示
Time Limit: 1000 ms Memory Limit: 256 MBDescription
高一一班的座位表是個n*m的矩陣,經過一個學期的相處,每個同學和前後左右相鄰的同學互相成為了好朋友。這學期要分文理科了,每個同學對於選擇文科與理科有著自己的喜悅值,而一對好朋友如果能同時選文科或者理科,那麼他們又將收穫一些喜悅值。作為電腦競賽教練的scp大老闆,想知道如何分配可以使得全班的喜悅值總和最大。
Input
第一行兩個正整數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列的同學同時選擇理科獲得的額外喜悅值。
Output
輸出一個整數,表示喜悅值總和的最大值
Sample Input 1 2
1 1
100 110
1
1000
Sample Output 1210
【範例說明】
兩人都選理,則獲得100+110+1000的喜悅值。
【資料規模】
對於100%以內的資料,n,m<=100 所有喜悅值均為小於等於5000的非負整數 Solution
這種相鄰格子的問題,一般都是考慮兩個相鄰的格子的最小割模型,然後把所有模型疊加起來。
現在考慮相鄰的兩人$x$和$y$。$x$選文或理的收益是$x_0,x_1$;$y$選文或理的收益是$y_0,y_1$;都選文的收益是$s_0$,都選理的收益是$s_1$。
那麼從總收益$sum=x_0+x_1+y_0+y_1+s_0+s_1$中減去最小割就是最優解。
考慮一下邏輯關係:
(1)如果$x$和$y$都選文,那麼需要割去$x_1,y_1,s_1$
(2)如果$x$和$y$都選理,那麼需要割去$x_0,y_0,s_0$
(3)如果$x$文$y$理,那麼需要割去$x_1,y_0,s_0,s_1$
(4)如果$x$理$y$文,那麼需要割去$x_0,y_1,s_0,s_1$
發現一個人選一科,必定割掉另外一科的喜悅值。那麼由源點$S$向$x$連$x_0$的邊,向$y$連$y_0$的邊;$x$向匯點$T$連$x_1$的邊,向$y$連$y_1$的邊。
現在,(1)和(2)還差$s_0$和$s_1$未刻畫。因為割掉兩條邊以後貌似圖就徹底分開了,不好再加入新的邊來體現,我們考慮將$s_0$附加在$x_0$與$y_0$上,將$s_1$附加在$x_1$與$y_1$上,即各分一半:
(1)和(2)刻畫完畢。但是(3)和(4)在其中不適用了。
如果$x$選文,$y$選理,那麼割去的邊是右上和左下兩條邊,其權值之和是$x_1+\frac{1}{2}s_1+y_0+\frac{1}{2}s_0$,但是我們期望的是$x_1+y_0+s_0+s_1$,不對啊。
怎麼辦?期望值和當前值一作差,得$\frac{1}{2}s_0+\frac{1}{2}s_1=\frac{1}{2}(s_0+s_1)$。想辦法把它執行個體化!當割去的是右上和左下兩條邊時,已經形成最小割,那麼強行把這條邊塞進去!由$y$向$x$連一條權值為$\frac{1}{2}(s_0+s_1)$的調整邊即可。
$x$理$y$文同理。
中間的兩條調整邊,僅在二者文理不同的時候起作用。現在這個模型,已經可以刻畫(1)~(4)所有的情況了。
對全圖進行建模,跑出最小割,用總收益和減去最小割即可。
Important:
我們不能對於每兩個格子都像如上模型一樣串連$(S,x),(S,y),(x,T),(y,T)$,將總收益和看做每兩個格子的$sum$之和,然後將模型並起來跑。
為什嗎?因為這樣我們會發現某一些$x_0$、$x_1$和$y_0$、$y_1$被多算了幾次,這是極不好的,模型之間出現了交集。
事實上對於一個點$x$,我們將所有$S$->$x$的邊都合并起來:即權值應該為:$x_0$或$y_0$加上$\frac{1}{2} \sum s_0$。$x$到$T$的邊同理。
而中間的調整邊照常即可。
這樣當$sum$為所有格子的收益和的時候,用$sum$減去最小割的答案是對的。
Tips:
可以將邊權乘上2,跑出最小割後除以2,即可忽略小數。
#include <cstdio>#include <queue>using namespace std;const int N=10010,INF=2147000000;int n,m,a[110][110],b[110][110],ax[110][110],ay[110][110],bx[110][110],by[110][110];int sum;int S,T,dis[N],cur[N],h[N],tot;queue<int> q;struct Edge{int v,next,f;}g[N*20];inline int id(int x,int y){return (x-1)*m+y;}inline int min(int x,int y){return x<y?x:y;}inline void addEdge(int u,int v,int f){ g[++tot].v=v; g[tot].f=f; g[tot].next=h[u]; h[u]=tot; g[++tot].v=u; g[tot].f=0; g[tot].next=h[v]; h[v]=tot;}bool bfs(){ while(!q.empty()) q.pop(); q.push(S); for(int i=1;i<=T;i++) dis[i]=-1; dis[S]=0; while(!q.empty()){ int u=q.front(); q.pop(); for(int i=h[u],v;i;i=g[i].next) if(g[i].f&&dis[v=g[i].v]==-1){ dis[v]=dis[u]+1; if(v==T) return true; q.push(v); } } return dis[T]!=-1;}int dfs(int u,int delta){ if(u==T) return delta; int ret=0,get; for(int i=cur[u],v;i&δi=g[i].next) if(g[i].f&&dis[v=g[i].v]==dis[u]+1){ get=dfs(v,min(delta,g[i].f)); g[i].f-=get; g[i^1].f+=get; if(g[i].f) cur[u]=i; delta-=get; ret+=get; } if(!ret) dis[u]=-1; return ret;}int dinic(){ int ret=0; while(bfs()){ for(int i=1;i<=T;i++) cur[i]=h[i]; ret+=dfs(S,INF); } return ret;}int main(){ scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) scanf("%d",&a[i][j]),sum+=a[i][j]; for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) scanf("%d",&b[i][j]),sum+=b[i][j]; for(int i=1;i<n;i++) for(int j=1;j<=m;j++) scanf("%d",&ax[i][j]),sum+=ax[i][j]; for(int i=1;i<n;i++) for(int j=1;j<=m;j++) scanf("%d",&bx[i][j]),sum+=bx[i][j]; for(int i=1;i<=n;i++) for(int j=1;j<m;j++) scanf("%d",&ay[i][j]),sum+=ay[i][j]; for(int i=1;i<=n;i++) for(int j=1;j<m;j++) scanf("%d",&by[i][j]),sum+=by[i][j]; S=n*m+1; T=n*m+2; tot=1; for(int i=1;i<=n;i++) for(int j=1;j<=m;j++){ int u=id(i,j); addEdge(S,u,a[i][j]*2+ax[i-1][j]+ax[i][j]+ay[i][j-1]+ay[i][j]); addEdge(u,T,b[i][j]*2+bx[i-1][j]+bx[i][j]+by[i][j-1]+by[i][j]); if(i<n){ addEdge(u,id(i+1,j),ax[i][j]+bx[i][j]); addEdge(id(i+1,j),u,ax[i][j]+bx[i][j]); } if(j<m){ addEdge(u,id(i,j+1),ay[i][j]+by[i][j]); addEdge(id(i,j+1),u,ay[i][j]+by[i][j]); } } int get=dinic(); get/=2; printf("%d\n",sum-get); return 0;}
奇妙代碼
【BZOJ2127】happiness