最小权值独立集 = 总权值 - 最小割
构图:首先要把图转换成二分图的形式,既左右两部分的点集是对立的。根据题意可以知道每一个点和相邻点都是对立的,所以如果把当前点划分到某个点集,则它的相邻点必在对立的点集里。所以,可以将横纵坐标之和为偶数的划分到一个点集里,奇数的划分到另一个点集。构造一个源点和一个汇点,将源点与其中一个点集的所有点连边,权值为该点的值,再将另一个点集的每个点都与汇点连边,权值也为点的值。将与源点连接的点集分别与其各自相邻的点相连,权值为无穷大。这样求出最小割即是两个点集中权值之和的最小值。所以答案为总权值-最小割。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define SIZE 512
#define inf 0x7fffffff
using namespace std;
struct node
{
int to,val,next;
}edge[SIZE*SIZE];
int N,sum,sc,sk,pt;
int map[32][32];
int head[SIZE],idx;
int gap[SIZE],dis[SIZE];
void addnode(int from,int to,int val)
{
edge[idx].to = to;
edge[idx].val = val;
edge[idx].next = head[from];
head[from] = idx ++;
edge[idx].to = from;
edge[idx].val = 0;
edge[idx].next = head[to];
head[to] = idx ++;
}
int dfs(int cur,int cval)
{
if(cur == sk)
return cval;
int mindis = pt - 1, tval = cval;
for(int i=head[cur]; i!=-1; i=edge[i].next)
{
int to = edge[i].to;
if(edge[i].val > 0)
{
if(dis[to] + 1 == dis[cur])
{
int val = dfs(to,min(edge[i].val,tval));
tval -= val;
edge[i].val -= val;
edge[i^1].val += val;
if(dis[sc] >= pt)
return cval-tval;
if(tval == 0)
break;
}
if(dis[to] < mindis)
mindis = dis[to];
}
}
if(cval == tval)
{
--gap[dis[cur]];
if(!gap[dis[cur]])
dis[sc] = pt;
dis[cur] = mindis + 1;
++gap[dis[cur]];
}
return cval-tval;
}
void sap()
{
memset(gap,0,sizeof(gap));
memset(dis,0,sizeof(dis));
int ret = 0;
gap[sc] = pt;
while(dis[sc] < pt)
ret += dfs(sc,inf);
printf("%d\n",sum-ret);
}
void read()
{
sum = idx = 0;
sc = 0, sk = N*N+1, pt = sk + 1;
memset(head,-1,sizeof(head));
int pos;
for(int i=1; i<=N; i++)
{
for(int j=1; j<=N; j++)
{
scanf("%d",&map[i][j]);
pos = (i-1)*N + j;
sum += map[i][j];
if((i+j)%2) addnode(pos,sk,map[i][j]);
else
{
addnode(sc,pos,map[i][j]);
if(i - 1 >= 1) addnode(pos,pos-N,inf);
if(i + 1 <= N) addnode(pos,pos+N,inf);
if(j - 1 >= 1) addnode(pos,pos-1,inf);
if(j + 1 <= N) addnode(pos,pos+1,inf);
}
}
}
}
int main()
{
while(~scanf("%d",&N))
{
read();
sap();
}
return 0;
}