[网络流24题] 方格取数问题 (最大权独立集---网络最小割)

734. [网络流24题] 方格取数问题
★★☆   输入文件:grid.in   输出文件:grid.out   简单对比
时间限制:1 s   内存限制:128 MB

«问题描述:
在一个有m*n 个方格的棋盘中,每个方格中有一个正整数。现要从方格中取数,使任
意2 个数所在方格没有公共边,且取出的数的总和最大。试设计一个满足要求的取数算法。
«编程任务:
对于给定的方格棋盘,按照取数要求编程找出总和最大的数。
«数据输入:
由文件grid.in提供输入数据。文件第1 行有2 个正整数m和n,分别表示棋盘的行数
和列数。接下来的m行,每行有n个正整数,表示棋盘方格中的数。
«结果输出:
程序运行结束时,将取数的最大总和输出到文件grid.out中。
输入文件示例 输出文件示例

grid.in
3 3
1 2 3
3 2 3
2 3 1

grid.out
11
(1<=N,M<=30) 

 

算法讨论: 图片原创:http://blog.csdn.net/water_glass/article/details/6853678

代码:

#include <cstdlib>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdio>
#include <vector>

using namespace std;
const int N = 900 + 5;
const int oo = 0x3f3f3f3f;

struct Edge {
	int from, to, cap, flow;
	Edge(int u = 0, int v = 0, int cap = 0, int flow = 0) :
		from(u), to(v), cap(cap), flow(flow) {}
};

struct Dinic {
	int nn, mm, s, t;
	int dis[N], cur[N], que[N * 10];
	bool vis[N];
	vector <Edge> edges;
	vector <int> G[N];
	
	void add(int from, int to, int cap) {
		edges.push_back(Edge(from, to, cap, 0));
		edges.push_back(Edge(to, from, 0, 0));
		mm = edges.size();
		G[from].push_back(mm - 2);
		G[to].push_back(mm - 1);
	}
	bool bfs() {
		int head = 1, tail = 1;
		memset(vis, false, sizeof vis);
		que[head] = s; dis[s] = 0; vis[s] = true;
		while(head <= tail) {
			int x = que[head];
			for(int i = 0; i < (signed) G[x].size(); ++ i) {
				Edge &e = edges[G[x][i]];
				if(!vis[e.to] && e.cap > e.flow) {
					vis[e.to] = true;
					dis[e.to] = dis[x] + 1;
					que[++ tail] = e.to;
				}
			}
			++ head;
		}
		return vis[t];
	}
	int dfs(int x, int a) {
		if(x == t || a == 0) return a;
		int flw = 0, f;
		for(int &i = cur[x]; i < (signed) G[x].size(); ++ i) {
			Edge &e = edges[G[x][i]];
			if(dis[e.to] == dis[x] + 1 && (f = dfs(e.to, min(a, e.cap - e.flow))) > 0) {
				e.flow += f; edges[G[x][i] ^ 1].flow -= f;
				a -= f; flw += f;
				if(!a) break;
			}
		}
		return flw;
	}
	int maxflow(int s, int t) {
		this->s = s; this->t = t;
		int flw = 0;
		while(bfs()) {
			memset(cur, 0, sizeof cur);
			flw += dfs(s, oo);
		}
		return flw;
	}
}net;

int n, m, S, T, sum, cnt;
int a[35][35], num[35][35];
int dx[]={0,1,-1,0}, dy[]={1,0,0,-1};

#define stone_e

int main() {
#ifndef stone_
	freopen("grid.in", "r", stdin);
	freopen("grid.out", "w", stdout);
#endif

	scanf("%d%d", &n, &m);
	S = 0; T = n * m + 1; net.nn = T;
	for(int i = 1; i <= n; ++ i)
	  for(int j = 1; j <= m; ++ j)
	    scanf("%d", &a[i][j]), sum += a[i][j];
	for(int i = 1; i <= n; ++ i)
	  for(int j = 1; j <= m; ++ j)
	    num[i][j] = ++ cnt;
	for(int i = 1; i <= n; ++ i)
	  for(int j = 1; j <= m; ++ j)
	    if((i + j) & 1) net.add(S, num[i][j], a[i][j]);
	    else net.add(num[i][j], T, a[i][j]);
	for(int i = 1; i <= n; ++ i)
		for(int j = 1; j <= m; ++ j)
			if((i + j) & 1) {
				for(int k = 0; k <= 3; ++ k) {
					int nx = dx[k] + i, ny = dy[k] + j;
					if(num[nx][ny])	net.add(num[i][j], num[nx][ny], oo);
				}
			}
	printf("%d\n", sum - net.maxflow(S, T));

#ifndef stone_
	fclose(stdin); fclose(stdout);
#endif
	return 0;
}

 

这个题WA了一遍是因为二分图染色之后连边不对了,标号后不能直接连。

1 2 3 4

5 6 7 8  按我原来的算法,左边的点集选的是1 3 5 7,但是看他们的位置,并不是错开的。

一个保险正确的写法是每次选择(i + j) & 1 位置上的数字,这样就错开了。

posted @ 2016-04-19 20:07  漫步者。!~  阅读(210)  评论(0编辑  收藏  举报