最小割...二分染色然后把颜色不同的点的源汇反过来..然后就可以做了.
某个点(x,y):
S->Id(x,y)(回报), Id(x,y)->T(代价), Id(i,j)&&Id(相邻节点)->newId(i,j)(+oo), newId(i,j)->T(回报)
然后染色不同的点反过来就可以了.
初始时答案为2*∑回报, 这样每个点要么割掉1个回报,要么割掉2个回报, 要么割掉1回报+代价.都对应着每一种方案
--------------------------------------------------------------------------------
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cctype>
using namespace std;
#define Id(x, y) ((x) * M + (y))
#define chk(x, y) (0 <= (x) && (x) < N && 0 <= (y) && (y) < M)
const int maxn = 5009;
const int INF = 1 << 30;
const int dx[4] = {-1, 0, 0, 1};
const int dy[4] = {0, 1, -1, 0};
inline int read() {
char c = getchar();
int ret = 0;
for(; !isdigit(c); c = getchar());
for(; isdigit(c); c = getchar()) ret = ret * 10 + c - '0';
return ret;
}
struct edge {
int to, cap;
edge *next, *rev;
} E[5000000], *pt = E, *head[maxn], *p[maxn], *cur[maxn];
inline void Add(int u, int v, int w) {
pt->to = v;
pt->cap = w;
pt->next = head[u];
head[u] = pt++;
}
inline void AddEdge(int u, int v, int w) {
Add(u, v, w);
Add(v, u, 0);
head[u]->rev = head[v];
head[v]->rev = head[u];
}
int N, M, S, T, V, ans;
int h[maxn], cnt[maxn];
void Solve() {
for(int i = 0; i < V; i++) cur[i] = head[i];
memset(cnt, 0, sizeof cnt);
memset(h, 0, sizeof h);
cnt[0] = V;
edge* e;
int Flow = 0;
for(int x = S, A = INF; h[S] < V; ) {
for(e = cur[x]; e; e = e->next)
if(e->cap && h[e->to] + 1 == h[x]) break;
if(e) {
A = min(A, e->cap);
cur[x] = p[e->to] = e;
if((x = e->to) == T) {
for(; x != S; x = p[x]->rev->to) {
p[x]->cap -= A;
p[x]->rev->cap += A;
}
Flow += A;
A = INF;
}
} else {
if(!--cnt[h[x]]) break;
h[x] = V;
for(e = head[x]; e; e = e->next) if(e->cap && h[e->to] + 1 < h[x]) {
h[x] = h[e->to] + 1;
cur[x] = e;
}
cnt[h[x]]++;
if(x != S)
x = p[x]->rev->to;
}
}
printf("%d\n", (ans << 1) - Flow);
}
void Init() {
N = read(); M = read();
V = N * M; S = V++; T = V++;
for(int i = 0; i < N; i++)
for(int j = 0; j < M; j++)
(i + j) & 1 ? AddEdge(Id(i, j), T, read()) : AddEdge(S, Id(i, j), read());
ans = 0;
for(int i = 0; i < N; i++)
for(int j = 0; j < M; j++) {
int v = read(), np = V++;
ans += v;
if((i + j) & 1) {
AddEdge(S, Id(i, j), v);
AddEdge(np, T, v);
AddEdge(Id(i, j), np, INF);
for(int k = 0; k < 4; k++) {
int x = i + dx[k], y = j + dy[k];
if(chk(x, y))
AddEdge(Id(x, y), np, INF);
}
} else {
AddEdge(Id(i, j), T, v);
AddEdge(S, np, v);
AddEdge(np, Id(i, j), INF);
for(int k = 0; k < 4; k++) {
int x = i + dx[k], y = j + dy[k];
if(chk(x, y))
AddEdge(np, Id(x, y), INF);
}
}
}
}
int main() {
Init();
Solve();
return 0;
}
--------------------------------------------------------------------------------
3774: 最优选择
Time Limit: 1 Sec Memory Limit: 128 MB
Submit: 96 Solved: 48
[Submit][Status][Discuss]
Description
小N手上有一个N*M的方格图,控制某一个点要付出Aij的代价,然后某个点如果被控制了,或者他周围的所有点(上下左右)都被控制了,那么他就算是被选择了的。一个点如果被选择了,那么可以得到Bij的回报,现在请你帮小N选一个最优的方案,使得回报-代价尽可能大。
Input
第一行两个正整数N,M表示方格图的长与宽。
接下来N行每行M个整数Aij表示控制的代价。
接下来N行每行M个整数Bij表示选择的回报。
Output
一个整数,表示最大的回报-代价(如果一个都不控制那么就是0)。
Sample Input
3 3
1 100 100
100 1 100
1 100 100
2 0 0
5 2 0
2 0 0
Sample Output
8
HINT
对于100%的数据,N,M<=50,Aij,Bij都是小于等于100的正整数。
Source