[国家集训队2011]happiness(最小割)
Link (https://www.luogu.org/problemnew/solution/P1646)
网络流中最大流与最小割的建图方式完全不同,比如本题中就要将选文理两科分连向S和T,最小割会将方阵中的点分别连向两点,即分成两类。再把联携收益S->tp->(i,j),(i,j+1)这样子,这样的话如果两格点在同一集合中,就会得到联携收益。(注意网络流不要写丑...)
1 #include <queue> 2 #include <cstdio> 3 #include <cstdlib> 4 #include <cstring> 5 #include <algorithm> 6 using namespace std; 7 int n,m,S,T,SUM=0; const int N=110,INF=0x3f3f3f3f; 8 int val; 9 // int v0[N][N],v1[N][N]; 10 // int e01[N][N],e02[N][N],e11[N][N],e12[N][N]; 11 12 //-----Qxx-----> 13 const int MAXN=N*N*6,MAXM=MAXN*12; 14 int head[MAXN],Next[MAXM],ver[MAXM],tot=1; int edge[MAXM]; 15 void link(int u,int v,int w){ 16 ver[++tot]=v,Next[tot]=head[u],head[u]=tot; edge[tot]=w; 17 ver[++tot]=u,Next[tot]=head[v],head[v]=tot; edge[tot]=0; 18 } 19 20 int p[N][N],cnt=0; //point's rank int the tot graph 21 void build() 22 { 23 scanf("%d %d",&n,&m); S=++cnt; T=++cnt; 24 for(int i=1;i<=n;i++){ 25 for(int j=1;j<=m;j++){ 26 scanf("%d",&val); SUM+=val; 27 p[i][j]=++cnt; link(S,p[i][j],val); 28 } 29 } 30 for(int i=1;i<=n;i++){ 31 for(int j=1;j<=m;j++){ 32 scanf("%d",&val); SUM+=val; 33 link(p[i][j],T,val); 34 } 35 } 36 37 for(int i=1;i<n;i++){ 38 for(int j=1;j<=m;j++){ 39 scanf("%d",&val); SUM+=val; 40 int tp=++cnt; link(S,tp,val); link(tp,p[i][j],INF); link(tp,p[i+1][j],INF); 41 } 42 } 43 for(int i=1;i<n;i++){ 44 for(int j=1;j<=m;j++){ 45 scanf("%d",&val); SUM+=val; 46 int tp=++cnt; link(tp,T,val); link(p[i][j],tp,INF); link(p[i+1][j],tp,INF); 47 } 48 } 49 50 for(int i=1;i<=n;i++){ 51 for(int j=1;j<m;j++){ 52 scanf("%d",&val); SUM+=val; 53 int tp=++cnt; link(S,tp,val); link(tp,p[i][j],INF); link(tp,p[i][j+1],INF); 54 } 55 } 56 for(int i=1;i<=n;i++){ 57 for(int j=1;j<m;j++){ 58 scanf("%d",&val); SUM+=val; 59 int tp=++cnt; link(tp,T,val); link(p[i][j],tp,INF); link(p[i][j+1],tp,INF); 60 } 61 } 62 } 63 64 //-----Dinic-----> 65 int d[MAXN]; 66 queue <int> Q; 67 bool bfs() 68 { 69 while(!Q.empty()) Q.pop(); 70 memset(d,0,sizeof(d)); 71 Q.push(S); d[S]=1; 72 while(!Q.empty()) 73 { 74 int x=Q.front(); Q.pop(); 75 for(int i=head[x];i;i=Next[i]){ 76 int v=ver[i]; 77 if(!d[v]&&edge[i]>0){ 78 d[v]=d[x]+1; 79 if(v==T) return true ; 80 Q.push(v); 81 } 82 } 83 } 84 return false ; 85 } 86 87 int cur[MAXN]; 88 int dfs(int x,int Flow) 89 { 90 if(x==T) return Flow; 91 int tmp=Flow; 92 for(int &i=cur[x];i&&tmp>0;i=Next[i]){ 93 int v=ver[i]; 94 if(d[v]==d[x]+1&&edge[i]>0){ 95 int k=dfs(v,min(edge[i],tmp)); 96 if(k==0){ d[v]=0; continue ; } 97 tmp-=k; edge[i]-=k,edge[i^1]+=k; 98 } 99 } 100 return Flow-tmp; 101 } 102 103 void work() 104 { 105 int minicut=0; 106 while(bfs()){ 107 for(int i=1;i<=cnt;i++) cur[i]=head[i]; 108 minicut+=dfs(S,INF); 109 } 110 printf("%d\n",SUM-minicut); 111 } 112 113 114 int main() 115 { 116 build(); 117 work(); 118 return 0; 119 }