[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 }

 

posted @ 2017-09-01 13:35  skylee03  阅读(149)  评论(0编辑  收藏  举报