[网络流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 位置上的数字,这样就错开了。

浙公网安备 33010602011771号