转载:http://blog.csdn.net/cold__v__moon/article/details/7924269 /* 这道题和方格取数2相似,是在方格取数2的基础上的变形。 方格取数2解法: 由题意知对于每一个方格,有选与不选,显然是二分的最大独立集,先求最小点权覆盖(它的补集恰好 是最大点权独立集),对于任何一条可行流 s->u->v->t, 在求最大流或最小割的时候,在这3条边中 至少选一条,将u->v设为inf,u->v就不可能存在于最小割中,就只是2选1,如果s->u或v->t选为最小割 则表示u或v(u和v可同时被选)属于最小点权覆盖,所以最小割=最小点权覆盖,再求补集就是最大点权独立集。 这道题的变形: 先考虑的答案的补集(类似最小点权覆盖,但不是),和方格取数不同的是可以去相邻的格子但是要减去2*(G[i][j]&G[a][b]); 所以对于答案的补集就是加上2*(G[i][j]&G[a][b]),对于任何一条可行流 s->u->v->t, 当u->v为2*(G[i][j]&G[a][b]),就可以 表示同时选取u,v的情况,对于答案的必选点s->u就是不存在割的s->u为inf; 推荐学习胡伯涛《最小割模型在信息学竞赛中的应用》,其中对最小割的模型应用讲的很清楚 */ #include <iostream> #include <memory.h> #include <stdio.h> using namespace std; const int maxn=55; const int maxm=maxn*maxn; const int inf=0x3fffffff; int G[maxn][maxn]; bool f[maxn][maxn]; int head[maxm],dis[maxm],gap[maxm],cur[maxm],e,pre[maxm]; int dir[4][2]={0,1,0,-1,1,0,-1,0}; int source,sink; struct { int t,next,w; }edge[maxm*6]; void add(int a,int b,int c) { //printf("%d->%d %d ",a,b,c); edge[e].t=b; edge[e].w=c; edge[e].next=head[a]; head[a]=e++; edge[e].t=a; edge[e].w=0; edge[e].next=head[b]; head[b]=e++; } int sap(int ncnt) { memset(dis,0,sizeof(dis)); memset(gap,0,sizeof(gap)); for(int i=0;i<=ncnt;i++) cur[i]=head[i]; int u=pre[source]=source,maxflow=0,aug=inf; gap[0]=ncnt; //P(); while(dis[source]<ncnt) { loop: for(int &i=cur[u];i!=-1;i=edge[i].next) { //P(); int v=edge[i].t; if(edge[i].w&&dis[u]==dis[v]+1) { aug=min(aug,edge[i].w); pre[v]=u; //printf("%d-> %d ",u,v); u=v; if(v==sink) { maxflow+=aug; for(u=pre[u];v!=source;v=u,u=pre[u]) { edge[cur[u]].w-=aug; edge[cur[u]^1].w+=aug; //printf("d"); } aug=inf; } goto loop; } } int mindis=ncnt; //P(); for(int i=head[u];i!=-1;i=edge[i].next) { int v=edge[i].t; if(edge[i].w&&mindis>dis[v]) { //printf("s"); cur[u]=i; mindis=dis[v]; } } if(--gap[dis[u]]==0) break; gap[dis[u]=mindis+1]++; u=pre[u]; } return maxflow; } int main() { int n,m,a,b,sum,k; while(~scanf("%d%d%d",&n,&m,&k)) { sum=0; for(int i=0;i<n;i++) for(int j=0;j<m;j++) { scanf("%d",&G[i][j]); sum+=G[i][j]; } memset(f,false,sizeof(f)); for(int i=0;i<k;i++) { scanf("%d%d",&a,&b); a--; b--; f[a][b]=true; } memset(head,-1,sizeof(head)); e=0; source=n*m; sink=n*m+1; for(int i=0;i<n;i++) for(int j=0;j<m;j++) if((i+j)%2==1) { add(source,i*m+j,f[i][j]?inf:G[i][j]); for(int k=0;k<4;k++) { a=i+dir[k][0]; b=j+dir[k][1]; if(a>=0&&a<n&&b>=0&&b<m) add(i*m+j,a*m+b,(G[i][j]&G[a][b])<<1); } } else add(i*m+j,sink,f[i][j]?inf:G[i][j]); printf("%d ",sum-sap(n*m+2)); } return 0; }