3 75 15 21 75 15 28 34 70 5
188
两种做法 :
点覆盖集:无向图G的一个点集,使得该图中所有边都至少有一个端点在该集合内。
// 最小点权覆盖集:在带点权无向图G中,点权之和最小的覆盖集。
// 点独立集:无向图G的一个点集,使得任两个在该集合中的点在原图中都不相邻。
// 最大点权独立集:在带权无向图G中,点权之和最大的独立集。
// 定理:
// 1. 最小点权覆盖集=最小割=最大流
// 2. 最大点权独立集=总权-最小点权覆盖集
1 . 对于整个图,把坐标和(x+y)是偶数的点放在一堆,坐标和是奇数的放一堆;因为整个图,坐标和为偶数的任意两点都不是相邻的,坐标和为奇数的也一样;
我们把坐标和为偶数的点连源点,容量为数值本身;坐标和为奇数的连汇点,容量为数值本身;
对于每个点,于它上下左右四个点连边,这四个点都是和它相邻的,所以不能喝它共存;容量为inf ;
得到建好的网络后,我们就可以就对网络求最小割,因为被割的边和最小,题目求最大,所以结果是 sum-sap();
#include<cstdio> #include<cstring> #include<map> #include<vector> #include<cmath> #include<cstdlib> #include<stack> #include<queue> #include <iomanip> #include<iostream> #include<algorithm> using namespace std ; const int N=450 ; const int M=N*8 ; const int inf = 1<<30; struct node { int u,v,c,next; }edge[M]; int pre[N],head[N],dis[N],cur[N],gap[N]; int top ; int move[4][2]={0, 1, 0, -1, 1, 0, -1, 0}; void add(int u ,int v,int c) { edge[top].u=u; edge[top].v=v; edge[top].c=c; edge[top].next=head[u]; head[u]=top++; edge[top].u=v; edge[top].v=u; edge[top].c=0; edge[top].next=head[v]; head[v]=top++; } int sap(int s,int t,int n) { int u,v,mindis,minflow=inf,flow=0; memset(gap,0,sizeof(gap)); memset(dis,0,sizeof(dis)); memset(pre,-1,sizeof(pre)); for(int i=0;i<n;i++) cur[i]=head[i] ; u=pre[s]=s; gap[0]=n; while(dis[s] <n) { loop: for(int &i=cur[u] ; i!=-1 ; i=edge[i].next) { v=edge[i].v; if(edge[i].c && dis[u]==dis[v]+1) { pre[v]=u; u=v; if(minflow > edge[i].c) minflow = edge[i].c ; if(v==t) { for(u=pre[v] ; v!=s ; v=u,u=pre[u]) { edge[cur[u]].c -=minflow ; edge[cur[u]^1].c += minflow ; } flow +=minflow ; minflow=inf; } goto loop ; } } mindis=n; for(int i=head[u];i!=-1 ; i=edge[i].next) { v=edge[i].v; if(edge[i].c>0 && dis[v] < mindis) { mindis=dis[v]; cur[u]=i; } } if((--gap[dis[u]]) ==0) break; gap[dis[u]=mindis+1]++; u=pre[u]; } return flow ; } int main() { int n ,x ,sum; while(~scanf("%d",&n)) { top=0;sum=0; memset(head,-1,sizeof(head)); int s=0,t=n*n+1 ; for(int i = 1 ; i <= n ; i++) for(int j = 1 ; j <= n ; j++) { int tmp=(i-1)*n+j ; scanf("%d",&x); if((i+j)%2==0) //坐标和为偶数 add(s,tmp,x); else add(tmp,t,x) ; sum+=x; } for(int i = 1 ; i <= n ; i++) for(int j = 1 ; j <= n ; j++) if((i+j)%2==0) //坐标和为偶数的点跟周围的点建边就可以把所有点都建完了; { int tmp=(i-1)*n+j; if(i>1) add(tmp,tmp-n,inf) ; //判断边界; if(i<n) add(tmp,tmp+n,inf) ; if(j<n) add(tmp,tmp+1,inf) ; if(j>1) add(tmp,tmp-1,inf) ; } int ans=sap(s,t,t+1); printf("%d\n",sum-ans) ; } return 0; }