首先要明白图论的几个定义:
点覆盖、最小点覆盖:
点覆盖集即一个点集,使得所有边至少有一个端点在集合里。或者说是“点” 覆盖了所有“边”。。
最小点覆盖(minimum vertex covering):
点最少的点覆盖。
点覆盖数(vertex covering number):
最小点覆盖的点数。
独立集:
独立集即一个点集,集合中任两个结点不相邻,则称V为独立集。或者说是导出的子图是零图(没有边)的点集。
最大独立集(maximum independent set):
点最多的独立集。
独立数(independent number):
最大独立集的点。
若把上面最小点覆盖和最大独立集中的端点数改成点的权值,分别就是最小点权覆盖和最大点权独立集的定义。
然后通过推导,我们可以证明一下公式:(具体证明请看胡伯涛《最小割模型在信息学竞赛中的应用》,这里只考虑应用)
最大点权独立集=总权值-最小点权覆盖集。
最小点权覆盖集=图的最小割值=最大流。
这道题很明显就是求最大点权独立集,所以直接套用公式即可。
建图:如果S与(i+j)%2==0的点相连,(i+j)%2==1的点与T相连,容量为该点的权值。(i+j)%==0与(i+j)%2==1的点相连,容量为无限大。
代码:
#include<stdio.h> #include<string.h> #include<iostream> using namespace std; const int INF=0x3f3f3f3f; const int N=444; const int M=N*N; int h[N],gap[N],head[N]; int cnt,n,m,s,t; int a[N][N]; struct node { int v,c,next; }e[M]; void init() { memset(head,-1,sizeof(head)); cnt=0; } void add(int u,int v,int w) { e[cnt].v=v,e[cnt].c=w; e[cnt].next=head[u];head[u]=cnt++; e[cnt].v=u,e[cnt].c=0; e[cnt].next=head[v];head[v]=cnt++; } int dfs(int u,int flow) { if(u==t) return flow; int c=flow,a,i,v,minh=t; for(i=head[u];i!=-1;i=e[i].next) { if(e[i].c) { v=e[i].v; if(h[v]==h[u]-1) { a=min(c,e[i].c); a=dfs(v,a); e[i].c-=a; e[i^1].c+=a; c-=a; if(h[s]>t) return flow-c; if(!c) break; } minh=min(minh,h[v]); } } if(c==flow) { if(--gap[h[u]]==0) h[s]=t+1; h[u]=minh+1; ++gap[h[u]]; } return flow-c; } int isap() { memset(gap,0,sizeof(gap)); memset(h,0,sizeof(h)); int ans=0;gap[0]=t+1; while(h[s]<=t) ans+=dfs(s,INF); return ans; } int main() { int i,j,sum; while(scanf("%d",&n)!=EOF) { sum=0;init();s=0,t=n*n+1; for(i=1;i<=n;i++) { for(j=1;j<=n;j++) { scanf("%d",&a[i][j]); sum+=a[i][j]; if((i+j)%2==0) { add(s,(i-1)*n+j,a[i][j]); if(i>1) add((i-1)*n+j,(i-2)*n+j,INF); if(j>1) add((i-1)*n+j,(i-1)*n+j-1,INF); if(i<n) add((i-1)*n+j,(i)*n+j,INF); if(j<n) add((i-1)*n+j,(i-1)*n+j+1,INF); } else add((i-1)*n+j,t,a[i][j]); } } printf("%d ",sum-isap()); } return 0; }