方格取数(1)
Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 4127 Accepted Submission(s): 1578
Problem Description
给你一个n*n的格子的棋盘,每个格子里面有一个非负数。
从中取出若干个数,使得任意的两个数所在的格子没有公共边,就是说所取的数所在的2个格子不能相邻,并且取出的数的和最大。
从中取出若干个数,使得任意的两个数所在的格子没有公共边,就是说所取的数所在的2个格子不能相邻,并且取出的数的和最大。
Input
包括多个测试实例,每个测试实例包括一个整数n 和n*n个非负数(n<=20)
Output
对于每个测试实例,输出可能取得的最大的和
Sample Input
3 75 15 21 75 15 28 34 70 5
Sample Output
188// 点覆盖集:无向图G的一个点集,使得该图中所有边都至少有一个端点在该集合内。 // 最小点权覆盖集:在带点权无向图G中,点权之和最小的覆盖集。 // 点独立集:无向图G的一个点集,使得任两个在该集合中的点在原图中都不相邻。 // 最大点权独立集:在带权无向图G中,点权之和最大的独立集。 // 定理: // 1. 最小点权覆盖集=最小割=最大流 // 2. 最大点权独立集=总权-最小点权覆盖集 // 解题: // 1. 先染色,取一个点染白色,和它相邻的点染黑色 // 2. 每个白点向它相邻的黑点连一条边,容量为 inf (无穷大) // 3. 增加源点S,向每一个白色点连一条边,容量为白点的权 // 4. 增加汇点T,每个黑点向T连一条边,容量为黑点的权除去最小割中的点,能保证余下的点构成一个点独立集。最后结果为最大点权独立集的点权总和,即 原图总的点权 - 最小割的点权总和(最大流)换一种说法,网络流割中的边,表示取数方案中不取的边,可以看成取数方案付出的代价。而最小割,则代表取数方案的最小代价,即取最优解时的代价。这题要求取数时不能取相邻的数,这样相邻两点之间建边容量为INF,代表如果同时选择这两点,代价为INF。AC代码:#include <iostream> #include <cmath> #include <cstdlib> #include <cstring> #include <cstdio> #include <queue> #include <ctime> #include <algorithm> #define ll __int64 using namespace std; const int INF = 1000000000; const int maxn = 405; int d[4][2] = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}}; struct Edge { int u, v, cap, flow, next; } et[maxn * maxn]; int low[maxn], cnt[maxn], dis[maxn], cur[maxn], pre[maxn], eh[maxn]; int s, t, n, num; bool ok(int x, int y) { if(x >= 1 && x <= n && y >= 1 && y <= n) return true; return false; } void init() { memset(eh, -1, sizeof(eh)); num = 0; } void add(int u, int v, int cap, int flow) { Edge e = {u, v, cap, flow, eh[u]}; et[num] = e; eh[u] = num++; } void addedge(int u, int v, int cap) { add(u, v, cap, 0); add(v, u, 0, 0); } int isap(int s, int t, int nv) { int u, v, now, flow = 0; memset(cnt, 0, sizeof(cnt)); memset(dis, 0, sizeof(dis)); memset(low, 0, sizeof(low)); for(u = 0; u <= nv; u++) cur[u] = eh[u]; low[s] = INF, cnt[0] = nv, u =s; while(dis[s] < nv) { for(now = cur[u]; now != -1; now = et[now].next) if(et[now].cap - et[now].flow && dis[u] == dis[v = et[now].v] + 1) break; if(now != -1) { cur[u] = pre[v] = now; low[v] = min(et[now].cap - et[now].flow, low[u]); u = v; if(u == t) { for(; u != s; u = et[pre[u]].u) { et[pre[u]].flow += low[t]; et[pre[u]^1].flow -= low[t]; } flow += low[t]; low[s] = INF; } } else { if(--cnt[dis[u]] == 0) break; dis[u] = nv, cur[u] = eh[u]; for(now = eh[u]; now != -1; now = et[now].next) if(et[now].cap - et[now].flow && dis[u] > dis[et[now].v] + 1) dis[u] = dis[et[now].v] + 1; cnt[dis[u]]++; if(u != s) u = et[pre[u]].u; } } return flow; } int main() { int val; while(~scanf("%d", &n)) { init(); s = 0; t = n * n + 1; int sum = 0; for(int i = 1; i <= n; i++) for(int j = 1; j <= n; j++) { scanf("%d", &val); if((i + j) &1) addedge((i - 1) * n + j, t, val); else addedge(s, (i - 1) * n + j, val); sum += val; } for(int i = 1; i <= n; i++) for(int j = 1; j <= n; j++) { if((i + j) &1) { for(int k = 0; k < 4; k++) { int nx = i + d[k][0], ny = j + d[k][1]; if(ok(nx, ny)) { addedge((nx - 1) * n + ny, (i - 1) * n + j, INF); } } } } printf("%d\n", sum - isap(s, t, t + 1)); } return 0; }