1. 程式人生 > >[hdu1569]方格取數(2) 最大點權獨立集

[hdu1569]方格取數(2) 最大點權獨立集

題目大意:給你一個m*n的格子的棋盤,每個格子裡面有一個非負數。
從中取出若干個數,使得任意的兩個數所在的格子沒有公共邊,就是說所取數所在的2個格子不能相鄰,並且取出的數的和最大。

我們首先將棋盤染成二色圖(相鄰點顏色不同),再將它想象成二分圖。

設立一個超級源點與超級點,將超級源點與一種顏色相連,另一種顏色與超級點相連,容量為格子權值。

再將將相鄰的異色格(從連線超級源點的格子到連線超級匯點的格子)之間建立一條容量無限大的邊。

我們就可以看出,題目要求求出二分圖的最大點權獨立集,將其轉為二分圖最小點權覆蓋問題(戳我)。

#include <cstdio>
#include <cstring>
#include <vector>
#include <queue>
#define inf 1e9
using namespace std;
int m,n;
int map[55][55];
struct Edge{
	int to,flow,ro;
};
vector<Edge> e;
vector<int> g[2505];
int dis[2505];
bool vis[2505];
int cur[2505];
int tot;
int s,ed;
void add_edge(int from,int to,int ro)
{
	e.push_back((Edge){to,0,ro});
	g[from].push_back(tot);
	tot++;
	e.push_back((Edge){from,0,0});
	g[to].push_back(tot);
	tot++;
}
void build_edge()
{
	register int i,j;
	for (i=1;i<=n;i++)
	{
		for (j=1;j<=m;j++)
		{
			int now=(i-1)*m+j;
			if ((i+j)%2)
			{
				add_edge(s,now,map[i][j]);
                if(i>1) add_edge(now,now-m,inf);
                if(i<n) add_edge(now,now+m,inf);
                if(j>1) add_edge(now,now-1,inf);
                if(j<m) add_edge(now,now+1,inf);
			}
			else add_edge(now,ed,map[i][j]);
		}
	}
}
bool bfs(int s,int ed)
{
	register int i;
	memset(vis,0,sizeof (vis));
	queue<int>q;
	q.push(s);
	vis[s]=1;dis[s]=0;
	while (!q.empty())
	{
		int x=q.front();
		q.pop();
		for (i=0;i<g[x].size();i++)
		{
 			Edge& ee=e[g[x][i]];
			if (!vis[ee.to]&&ee.ro>ee.flow)
			{
				vis[ee.to]=1;
				dis[ee.to]=dis[x]+1;
				q.push(ee.to);
			}
		}
	}
	return vis[ed];
}
int dfs(int x,int a)
{
	if (x==ed||a==0) return a;
	int flow=0,f;
	for (int& i=cur[x];i<g[x].size();i++)
	{
		Edge& ee=e[g[x][i]];
		if(dis[x]+1==dis[ee.to]&&(f=dfs(ee.to,min(a,ee.ro-ee.flow)))>0)
		{
			ee.flow+=f;
			e[g[x][i]^1].flow-=f;
			flow+=f;
			a-=f;
			if (a==0) break;
		}
 	}
 	return flow;
}
int dinic(int s,int ed)
{
	int flow=0;
	while (bfs(s,ed))
	{
		memset(cur,0,sizeof(cur));
		flow+=dfs(s,inf);
	}
	return flow;
}
int main()
{
	register int i,j;
	while(~scanf("%d%d",&n,&m))
	{
		memset(dis,0,sizeof(dis));
		for (i=0;i<=2503;i++)
		{
			g[i].clear();
		}
		e.clear();
		tot=0;
		int re=0;
		for (i=1;i<=n;i++)
		for (j=1;j<=m;j++)
		{
			scanf("%d",&map[i][j]);
			re+=map[i][j];
		}
		s=0,ed=n*m+1;
		build_edge();
		printf("%d\n",re-dinic(s,ed));
	}
	return 0;
}