[国家集训队]happiness
题目:洛谷P1646、BZOJ2127。
题目大意:
高一一班的座位表是个n*m的矩阵,经过一个学期的相处,每个同学和前后左右相邻的同学互相成为了好朋友。这学期要分文理科了,每个同学对于选择文科与理科有着自己的喜悦值,而一对好朋友如果能同时选文科或者理科,那么他们又将收获一些喜悦值。求最大可能的喜悦值之和。
解题思路:
比较恶心的最小割。
首先,从源点向每个人连容量为(选文科所得的喜悦值)的边,从每个人向汇点连容量为(选理科所得的喜悦值)。
然后,对于相邻的两个人x和y,建一个新的点k,若他们都选文科,则从源点向k连容量为(产生的喜悦值)的边,再从k向x、y分别连容量为inf的边;若他们都选理科,则从k向汇点连容量为(产生的喜悦之)的边,再从x、y分别向k连容量为inf的边(不能理解的话可以话草图割一割)。
跑最大流,然后用所有读入的喜悦值的总和减去最大流即可。
C++ Code:
#include<bits/stdc++.h> #define S 0 #define T 600001 #define inf 0x3f3f3f3f inline int readint(){ int c=getchar(),d=0; for(;!isdigit(c);c=getchar()); for(;isdigit(c);c=getchar()) d=(d<<3)+(d<<1)+(c^'0'); return d; } struct edge{ int to,cap,nxt; }e[3000000]; int n,m,head[600005],cnt=1,s=0,level[600005],iter[600005]; inline int addedge(const int u,const int v,const int f){ e[++cnt]=(edge){v,f,head[u]}; head[u]=cnt; e[++cnt]=(edge){u,0,head[v]}; head[v]=cnt; if(f!=inf) s+=f; } inline int num(const int x,const int y){return n*(y-1)+x;} std::queue<int>q; void bfs(){ q.push(S); level[S]=1; while(!q.empty()){ int u=q.front();q.pop(); for(int i=head[u];i!=-1;i=e[i].nxt) if(e[i].cap&&level[e[i].to]==-1){ level[e[i].to]=level[u]+1; q.push(e[i].to); } } } inline int min(const int a,const int b){return a<b?a:b;} int dfs(const int u,const int t,const int f){ if(f==0||u==t)return f; for(int& i=iter[u];i!=-1;i=e[i].nxt) if(e[i].cap&&level[e[i].to]==1+level[u]){ int d=dfs(e[i].to,t,min(e[i].cap,f)); if(d){ e[i].cap-=d; e[i^1].cap+=d; return d; } } return 0; } int dinic(){ for(int f,flow=0;;){ memset(level,-1,sizeof level); bfs(); if(level[T]==-1)return flow; memcpy(iter,head,sizeof iter); while(f=dfs(S,T,inf))flow+=f; } } int main(){ n=readint(),m=readint(); memset(head,-1,sizeof head); int CNT=n*m+1; for(int i=1;i<=n;++i) for(int j=1;j<=m;++j)addedge(S,num(i,j),readint()); for(int i=1;i<=n;++i) for(int j=1;j<=m;++j)addedge(num(i,j),T,readint()); for(int i=1;i<n;++i) for(int j=1;j<=m;++j){ int x=num(i,j),y=num(i+1,j); addedge(++CNT,x,inf); addedge(CNT,y,inf); addedge(S,CNT,readint()); } for(int i=1;i<n;++i) for(int j=1;j<=m;++j){ int x=num(i,j),y=num(i+1,j); addedge(x,++CNT,inf); addedge(y,CNT,inf); addedge(CNT,T,readint()); } for(int i=1;i<=n;++i) for(int j=1;j<m;++j){ int x=num(i,j),y=num(i,j+1); addedge(++CNT,x,inf); addedge(CNT,y,inf); addedge(S,CNT,readint()); } for(int i=1;i<=n;++i) for(int j=1;j<m;++j){ int x=num(i,j),y=num(i,j+1); addedge(x,++CNT,inf); addedge(y,CNT,inf); addedge(CNT,T,readint()); } printf("%d\n",s-dinic()); return 0; }