[BZOJ2127]happiness
题目大意:
有一个$n\times m$的阵列,每个位置上有一元素,现在要将这些元素分为$A$部和$B$部,
对于每个元素$i$,如果它被分到了$A$部,就会得到相应的收益$a_i$;如果它被分到了$B$部,也会得到相应的收益$b_i$。
对于每两个相邻的元素$i$和$j$,如果被同时分在$A$部或$B$部,也会有相应的额外收益$c_{ij}$,$d_{ij}$。
思路:
考虑最小割将所有元素分$S$集和$T$集,$S$集表示$A$部,$T$集表示$B$部,
对于每个元素$i$,连一条从$S$到$i$的容量为$a_i$的边,再连一条从$i$到$T$的容量为$b_i$的边,若其中一条被割去,则说明该元素不在那个集合,易证在最小割中,这两条边有且仅有一条会被割去。
对于每两个相邻的元素$i$和$j$,设$flow(x,y)$表示下面新加入的边的容量,分以下三种情况考虑:
1.两个元素都被分在了$A$部,那么两人加入$B$部得到的收益应当被加入到割集中,除了割去原有的边以外,还需要割去两个元素共同加入$B$集的收益,因此只要增加满足$flow(i,T)+flow(j,T)=c_{ij}$的边即可。
2.两个元素都被分在了$B$部,同上得$flow(S,i)+flow(S,j)=d_{ij}$。
3.两个元素属于不同集合,假设$i$属于$A$部,$j$属于$B$部,那么我们可以发现,两者都属于$A$部或$B$部的额外收益都得被割掉,因此我们需要保证新加入的边$flow(S,i)+flow(i,j)+flow(j,T)=c_{ij}+d_{ij}$。同理,若$j$属$A$部,$i$属$B$部,则需要保证$flow(S,j)+flow(j,i)+flow(i,T)=c_{ij}+d_{ij}$。
然后跑一遍最小割,就得到了不能被满足的那些收益,答案即为总收益-最小割。
细节:
1.因为建图时的规则比较多,可能会导致两点见被连了多条边,因此可以将两点之间的边合并成一条。
2.合并的时候不能直接用一个$V\times V$的数组存($V$为原图中点数),这样会MLE,用map或者hash_map的也不好。
3.因为除了$S$和$T$外,其它的元素之间只有相邻的四个点才可能有边,因此可以考虑只记录相邻元素的边的情况,这样内存比较小,时间也会比较快。
4.lych有一个神奇的加边方法,就是先用数组把输入数据存起来,然后加边的时候统一计算即可,代码只有57行。
1 #include<queue> 2 #include<cstdio> 3 #include<cctype> 4 #include<vector> 5 #include<cstring> 6 inline int getint() { 7 char ch; 8 while(!isdigit(ch=getchar())); 9 int x=ch^'0'; 10 while(isdigit(ch=getchar())) x=(((x<<2)+x)<<1)+(ch^'0'); 11 return x; 12 } 13 const int inf=0x7fffffff; 14 int s,t; 15 const int V=10002,E=80000; 16 int ss[V]={0},left[V]={0},right[V]={0},up[V]={0},down[V]={0},tt[V]={0}; 17 struct Edge { 18 int to,remain; 19 }; 20 Edge e[E]; 21 int sz=0; 22 std::vector<int> g[V]; 23 inline void add_edge(const int u,const int v,const int w) { 24 e[sz]=(Edge){v,w}; 25 g[u].push_back(sz); 26 sz++; 27 } 28 int lev[V]; 29 inline void bfs() { 30 std::fill(&lev[1],&lev[t+1],-1); 31 lev[s]=0; 32 std::queue<int> q; 33 q.push(s); 34 while(!q.empty()) { 35 int x=q.front(); 36 q.pop(); 37 for(unsigned i=0;i<g[x].size();i++) { 38 Edge &y=e[g[x][i]]; 39 if(y.remain&&!~lev[y.to]) { 40 lev[y.to]=lev[x]+1; 41 q.push(y.to); 42 } 43 } 44 } 45 } 46 unsigned cur[V]; 47 int dfs(const int x,const int flow) { 48 if(x==t) return flow; 49 for(unsigned &i=cur[x];i<g[x].size();i++) { 50 Edge &y=e[g[x][i]]; 51 if(y.remain&&lev[x]<lev[y.to]) { 52 if(int f=dfs(y.to,std::min(flow,y.remain))) { 53 e[g[x][i]].remain-=f; 54 e[g[x][i]^1].remain+=f; 55 return f; 56 } 57 } 58 } 59 return 0; 60 } 61 inline int Dinic() { 62 int maxflow=0; 63 for(;;) { 64 bfs(); 65 if(!~lev[t]) break; 66 memset(cur,0,sizeof cur); 67 while(int flow=dfs(s,inf)) { 68 maxflow+=flow; 69 } 70 } 71 return maxflow; 72 } 73 int main() { 74 int n=getint(),m=getint(); 75 s=0,t=n*m+1; 76 int sum=0; 77 for(int i=1;i<=n;i++) { 78 for(int j=1;j<=m;j++) { 79 int w=getint()<<1; 80 sum+=w; 81 ss[(i-1)*m+j]+=w; 82 } 83 } 84 for(int i=1;i<=n;i++) { 85 for(int j=1;j<=m;j++) { 86 int w=getint()<<1; 87 sum+=w; 88 tt[(i-1)*m+j]+=w; 89 } 90 } 91 for(int i=1;i<n;i++) { 92 for(int j=1;j<=m;j++) { 93 int w=getint(); 94 sum+=w<<1; 95 down[(i-1)*m+j]+=w; 96 up[i*m+j]+=w; 97 ss[(i-1)*m+j]+=w; 98 ss[i*m+j]+=w; 99 } 100 } 101 for(int i=1;i<n;i++) { 102 for(int j=1;j<=m;j++) { 103 int w=getint(); 104 sum+=w<<1; 105 down[(i-1)*m+j]+=w; 106 up[i*m+j]+=w; 107 tt[(i-1)*m+j]+=w; 108 tt[i*m+j]+=w; 109 } 110 } 111 for(int i=1;i<=n;i++) { 112 for(int j=1;j<m;j++) { 113 int w=getint(); 114 sum+=w<<1; 115 right[(i-1)*m+j]+=w; 116 left[(i-1)*m+j+1]+=w; 117 ss[(i-1)*m+j]+=w; 118 ss[(i-1)*m+j+1]+=w; 119 } 120 } 121 for(int i=1;i<=n;i++) { 122 for(int j=1;j<m;j++) { 123 int w=getint(); 124 sum+=w<<1; 125 right[(i-1)*m+j]+=w; 126 left[(i-1)*m+j+1]+=w; 127 tt[(i-1)*m+j]+=w; 128 tt[(i-1)*m+j+1]+=w; 129 } 130 } 131 for(int i=1;i<=n;i++) { 132 for(int j=1;j<=m;j++) { 133 add_edge(s,(i-1)*m+j,ss[(i-1)*m+j]); 134 add_edge((i-1)*m+j,s,0); 135 } 136 } 137 for(int i=1;i<n;i++) { 138 for(int j=1;j<m;j++) { 139 add_edge((i-1)*m+j,i*m+j,down[(i-1)*m+j]); 140 add_edge(i*m+j,(i-1)*m+j,up[i*m+j]); 141 add_edge((i-1)*m+j,(i-1)*m+j+1,right[(i-1)*m+j]); 142 add_edge((i-1)*m+j+1,(i-1)*m+j,left[(i-1)*m+j+1]); 143 } 144 } 145 for(int i=1;i<n;i++) { 146 add_edge(i*m,(i+1)*m,down[i*m]); 147 add_edge((i+1)*m,i*m,up[(i+1)*m]); 148 } 149 for(int j=1;j<m;j++) { 150 add_edge((n-1)*m+j,(n-1)*m+j+1,right[(n-1)*m+j]); 151 add_edge((n-1)*m+j+1,(n-1)*m+j,left[(n-1)*m+j+1]); 152 } 153 for(int i=1;i<=n;i++) { 154 for(int j=1;j<=m;j++) { 155 add_edge((i-1)*m+j,t,tt[(i-1)*m+j]); 156 add_edge(t,(i-1)*m+j,0); 157 } 158 } 159 printf("%d\n",(sum-Dinic())>>1); 160 return 0; 161 }