Description
给你一个n*n的格子的棋盘,每个格子里面有一个非负数。
从中取出若干个数,使得任意的两个数所在的格子没有公共边,就是说所取的数所在的2个格子不能相邻,并且取出的数的和最大。
解题思路
分析下题目的一些关键的特点:
首先此题最大特点就是一个点只和周围4个点有关联,简单的说就是可以通过一个奇偶染色构造二分图(为么么?),在假设如果每个的点权都为1,那此题就可以转换成最大独立集。那现在这是个什么问题呢?
其实这是一个二分图最大点权独立集问题,就是找出图中一些点,使得这些点之间没有边相连,这些点的权值之和最大。独立集与覆盖集是互补的,求最大点权独立集可以转化为求最小点权覆盖集(最小点权支配集)。最小点权覆盖集问题可以转化为最小割问题解决。
结论:
最大点权独立集 = 所有点权 - 最小点权覆盖集 = 所有点权 - 最小割集 = 所有点权 -网络最大流。
结论的简单说明:
对于一个网络,除去冗余点(不存在一条ST路径经过的点),每个顶点都在一个从S到T的路径上。
割的性质就是不存在从S到T的路径,简单割可以认为割边关联的非ST节点为割点,
而在二分图网络流模型中每个点必关联到一个割点(否则一定还有增广路,当前割不成立),
所以一个割集对应了一个覆盖集(支配集)。最小点权覆盖集就是最小简单割,求最小简单割
的建模方法就是把XY集合之间的变容量设为无穷大,此时的最小割就是最小简单割了
对此,我们对这个问题的模型基本构造完毕!
代码实现
HDU1565http://acm.hdu.edu.cn/showproblem.php?pid=1565的图比较小,我采用了邻接矩阵+EK算法求最大流:
//============================================================================
// Name : 1001.cpp
// Author :
// Version :
// Copyright : Your copyright notice
// Description : Hello World in C++, Ansi-style
//============================================================================
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <vector>
#include <algorithm>
using namespace std;
const int inf=0x7fffffff;
const int MAXN=25;
int dir[4][2]={{0,1},{0,-1},{1,0},{-1,0}};
struct Node{
int x,y;
};
int val[MAXN][MAXN];
int grap[MAXN*MAXN][MAXN*MAXN];
int n,m;
inline bool Judge(Node a)
{
return (a.x>=0&&a.x<n&&a.y>=1&&a.y<=n);
}
void Setup()
{
memset(grap,0,sizeof(grap));
for(int i=0;i<n;i++)
{
for(int j=1;j<=n;j++)
{
//下面是二分这个图,根据奇偶性
if((i+j)&1)
grap[0][i*n+j]=val[i][j];//源点0到X集合的流
else
grap[i*n+j][n*n+1]=val[i][j];//Y集合到汇点的流
}
}
Node next;
for(int i=0;i<n;i++)
{
for(int j=1;j<=n;j++)
{
for(int k=0;k<4;k++)
{
next.x=i+dir[k][0];
next.y=j+dir[k][1];
if(Judge(next))
{
//构造X/Y集合之间的流,注意流要是一个方向的
//否则最小割不会是最小点权覆盖集
if((i+j)&1)
grap[i*n+j][next.x*n+next.y]=inf;
}
}
}
}
}
int Edmonds_Karp(int s,int t)
{
int p,q,que[MAXN*MAXN],u,v,pre[MAXN*MAXN],flow=0,aug;
while(true)
{
memset(pre,-1,sizeof(pre));
for(que[p=q=0]=s;p<=q;p++)
{
u=que[p];
for(v=0;v<m && pre[t]<0;v++)
{
if(grap[u][v]>0 && pre[v]<0)
pre[v]=u,que[++q]=v;
}
if(pre[t]>=0) break;
}
if(pre[t]<0) break;
aug=inf;
for(u=pre[v=t];v!=s;v=u,u=pre[u])
if(grap[u][v]<aug)
aug=grap[u][v];
for(u=pre[v=t];v!=s;v=u,u=pre[u])
{
grap[u][v]-=aug,grap[v][u]+=aug;
}
flow+=aug;
}
return flow;
}
int main() {
while(scanf("%d",&n)!=EOF)
{
int sum=0;
for(int i=0;i<n;i++)
{
for(int j=1;j<=n;j++)
{
scanf("%d",&val[i][j]);
sum+=val[i][j];
}
}
m=n*n+2;//m为顶点数
Setup();
int flg=Edmonds_Karp(0,n*n+1);
printf("%d\n",sum-flg);
}
return 0;
}
HDU1569http://acm.hdu.edu.cn/showproblem.php?pid=1569数据比较大,用邻接矩阵会超内存!所以采用边集+Dinic算法求最大流
//============================================================================
// Name : 1001.cpp
// Author :
// Version :
// Copyright : Your copyright notice
// Description : Hello World in C++, Ansi-style
//============================================================================
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <vector>
#include <algorithm>
using namespace std;
const int inf = 0x7fffffff;
const int MAXN = 55;
int dir[4][2] = { { 0, 1 }, { 0, -1 }, { 1, 0 }, { -1, 0 } };
struct Node {
int x, y;
};
struct edge {
int x, y, nxt, c;
} bf[1001000];
int ne, head[2505], cur[2505], ps[2505], dep[2505];
void addedge(int x, int y, int c) {
bf[ne].x = x;
bf[ne].y = y;
bf[ne].c = c;
bf[ne].nxt = head[x];
head[x] = ne++;
bf[ne].x = y;
bf[ne].y = x;
bf[ne].c = 0;
bf[ne].nxt = head[y];
head[y] = ne++;
}
int maxflow(int nd, int s, int t) {
int tr, res = 0;
int i, j, k, f, r, top;
while (true) {
memset(dep, -1, sizeof(dep));
for (f = dep[ps[0] = s] = 0, r = 1; f != r;) {
for (i = ps[f++], j = head[i]; j; j = bf[j].nxt) {
if (bf[j].c && -1 == dep[k = bf[j].y]) {
dep[k] = dep[i] + 1;
ps[r++] = k;
if (k == t) {
f = r;
break;
}
}
}
}
if (-1 == dep[t])
break;
memcpy(cur, head, nd * sizeof(int));
for (i = s, top = 0;;) {
if (i == t) {
for (k = 0, tr = inf; k < top; ++k)
if (bf[ps[k]].c < tr)
tr = bf[ps[f = k]].c;
for (k = 0; k < top; ++k)
bf[ps[k]].c -= tr, bf[ps[k] ^ 1].c += tr;
res += tr;
i = bf[ps[top = f]].x;
}
for (j = cur[i]; cur[i]; j = cur[i] = bf[cur[i]].nxt)
if (bf[j].c && dep[i] + 1 == dep[bf[j].y])
break;
if (cur[i]) {
ps[top++] = cur[i];
i = bf[cur[i]].y;
} else {
if (0 == top)
break;
dep[i] = -1;
i = bf[ps[--top]].x;
}
}
}
return res;
}
int val[MAXN][MAXN];
//int grap[MAXN * MAXN][MAXN * MAXN];
int n, m, nodes;
inline bool Judge(Node a) {
return (a.x >= 0 && a.x < n && a.y >= 1 && a.y <= m);
}
void Setup() {
ne=2;
memset(head,0,sizeof(head));
memset(bf, 0, sizeof(bf));
for (int i = 0; i < n; i++) {
for (int j = 1; j <= m; j++) {
if ((i + j) & 1) {
addedge(0,i*m+j,val[i][j]);
// grap[0][i * m + j] = val[i][j];
} else {
addedge(i*m+j,n*m+1,val[i][j]);
// grap[i * m + j][n * m + 1] = val[i][j];
}
}
}
Node next;
for (int i = 0; i < n; i++) {
for (int j = 1; j <= m; j++) {
for (int k = 0; k < 4; k++) {
next.x = i + dir[k][0];
next.y = j + dir[k][1];
if (Judge(next)) {
if ((i + j) & 1)
{
addedge(i*m+j,next.x*m+next.y,inf);
// grap[i * m + j][next.x * m + next.y] = inf;
}
}
}
}
}
}
int main() {
while (scanf("%d%d", &n, &m) != EOF) {
int sum = 0;
for (int i = 0; i < n; i++) {
for (int j = 1; j <= m; j++) {
scanf("%d", &val[i][j]);
sum += val[i][j];
}
}
nodes = n * m + 2;
Setup();
int flg = maxflow(nodes,0,n*m+1);
// printf("maxflow=%d\n",flg);
printf("%d\n", sum - flg);
}
return 0;
}