HDU-1565 方格取数(1) 网络流

http://acm.hdu.edu.cn/showproblem.php?pid=1565

题意:

给你一个n*n的格子的棋盘,每个格子里面有一个非负数。
从中取出若干个数,使得任意的两个数所在的格子没有公共边,就是说所取的数所在的2个格子不能相邻,并且取出的数的和最大。

算法分析:
最大独立点集 = 总点数 - 最小点覆盖集
 
独立集:
独立集是指图的顶点集的一个子集,该子集的导出子图不含边.如果一个独立集不是任何一个独立集的子集, 那么称这个独立集是一个极大独立集.一个图中包含顶点数目最多的独立集称为最大独立集。最大独立集一定是极大独立集,但是极大独立集不一定是最大的独立集。

支配集:

独立集相对应的就是支配集,支配集也是图顶点集的一个子集,设S 是图G 的一个支配集,则对于图中的任意一个顶点u,要么属于集合s, 要么与s 中的顶点相邻。在s中除去任何元素后s不再是支配集,则支配集s是极小支配集。称G的所有支配集中顶点个数最少的支配集为最小支配集,最小支配集中的顶点个数成为支配数。

最小点的覆盖:

最小点的覆盖也是图的顶点集的一个子集,如果我们选中一个点,则称这个点将以他为端点的所有边都覆盖了。将图中所有的边都覆盖所用顶点数最少,这个集合就是最小的点的覆盖。

最大团:

图G的顶点的子集,设D是最大团,则D中任意两点相邻。若u,v是最大团,则u,v有边相连,其补图u,v没有边相连,所以图G的最大团=其补图的最大独立集。

 

一些性质:

最大独立集+最小覆盖集=V

最大团=补图的最大独立集

最小覆盖集=最大匹配


多采用奇偶建立二分图,题目相当于从图中选出一点集,使之点权最大,并且集合中任意点对都不含连边,这恰恰是二分图的最大权独立集。最大独立集 = 总点权 - 最小点权覆盖集。 最小点覆盖集 = 最大匹配;

对二分图建网络:

先给二分图加入额外的源点s和汇点t,将匹配以一条s->u->v->t形式的流路径串联起来,将点权转移到边的容量上,参考最小割最大流定理,就能用最大流解决最小点权覆盖集问题。

建边时脑子一定要清楚。相邻点要染不同的颜色,源点为0,汇点为最后的那个点。染色方法基本上是判断if(边+边)%2==0源点与其连接,反之汇点与其连接。如果两点相邻的话,连接值为INF,无穷大。


EK:

#include "stdio.h"
#include "queue"
#include "string.h"
#include "vector"
#include "algorithm"
using namespace std;
const int maxn = 450;
const int inf = 1<<30;
int n,m;
int cap[maxn][maxn],flow[maxn][maxn];
int map[maxn][maxn];
int a[maxn],path[maxn];

bool BFS( int st,int end )
{
	queue<int>que;
	memset( a,0,sizeof(a) );
	for( int i = 0; i <= n; i ++ )
		path[i] = i;
	a[st] = inf;
	que.push(st);
	while( !que.empty() ){
		int u = que.front();  que.pop();
		for( int v = 0; v <= n; v ++ ){
			if( !a[v] && cap[u][v] > flow[u][v] ){
				path[v] = u;
				que.push(v);
				a[v] = a[u] < cap[u][v]-flow[u][v]?a[u]:cap[u][v]-flow[u][v];
			}
		}
	}
	return a[end];
}

int EK_Max_Flow( int st,int end )
{
	int ans_Flow = 0;
	int mn;
	while( BFS( st,end ) ){
		mn = a[end];
		for( int i = end; i != st; i = path[i] ){
			flow[path[i]][i] += mn;
			flow[i][path[i]] -= mn;
		}
		ans_Flow += mn;
	}
	return ans_Flow;
}

int main()
{
	//freopen("data.txt","r",stdin);
	int sum;
    while( scanf("%d",&m) != EOF ){
		sum = 0;
		memset( cap,0,sizeof(cap) );
		memset( flow,0,sizeof(flow) );
		for( int i = 1; i <= m; i ++ ){
			for( int j = 1; j <= m; j ++ ){
				scanf("%d",&map[i][j]);
				sum += map[i][j];
			}
		}
		int s = 0;
		n = m*m+1;
		for( int i = 1; i <= m; i ++ ){
			for( int j = 1; j <= m; j ++ ){
				if( (i+j)%2 )
					cap[(i-1)*m+j][n] = map[i][j]; 
				else
					cap[s][(i-1)*m+j] = map[i][j];
			}
		}
		for( int i = 1; i <= m; i ++ ){
			for( int j = 1; j <= m; j ++ ){
				if( (i+j)%2 == 0 ){
					if( j < m )	cap[(i-1)*m+j][(i-1)*m+j+1] = inf;
					if( j > 1 )	cap[(i-1)*m+j][(i-1)*m+j-1] = inf;
					if( i < m ) cap[(i-1)*m+j][i*m+j] = inf;
					if( i > 1 ) cap[(i-1)*m+j][(i-2)*m+j] = inf;
				}
			}
		}
		printf("%d\n",sum - EK_Max_Flow(s,n));
	} 
    return 0;
}



  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值