zoukankan      html  css  js  c++  java
  • [网络流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
    ", 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 位置上的数字,这样就错开了。

  • 相关阅读:
    日本最大的汽车品牌:丰田【仅供自己参考】
    读书笔记1
    读书笔记1
    计算机网络笔记1
    ZY凉凉经
    HK凉凉经
    访问一个网站,发生了什么?
    正向代理VS反向代理
    mac下打开hosts文件
    国际手机区号
  • 原文地址:https://www.cnblogs.com/sxprovence/p/5409883.html
Copyright © 2011-2022 走看看