題目: LINK
題目大意: 一個 N*M 的網格,每個單元都有一塊價值 Cij 的寶石。問最多能取多少價值的寶石且任意兩塊寶石不相鄰。(1 <= N, M <= 50, 0 <= Cij <= 40000)
最大點權獨立集問題
先將網格黑白染色,從源點S到每個黑點有一條邊,從每個白點到匯點T有一條邊,容量均爲相應寶石的價值。
每個黑點向與其相鄰的四個白點連邊,容量爲∞。設最小割爲 ans,結果即爲∑Cij – ans。
因爲每一個割都會對應一個方案。假設割爲[S,T],割上的邊要麼與S連接,要麼與T連接,因爲其他邊都爲∞。
割會分割成兩部分含S的部分 和 含T的部分,兩部分不相連接,我們取的點就是 和S相連的黑點的值A' + 和T點相連的白點的值B'。
結果= A' + B' = sum - [S, T];
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <string>
#include <vector>
#include <cmath>
#include <queue>
#include <map>
#include <set>
using namespace std;
#define INF 1000000000
//typedef __int64 LL;
#define N 3333
int t, n, m, lev[N], tot, hh[N], num[55][55], S, T, ids[55][55];
struct node {
int u, v, w, next;
}edge[1000001];
int dir[4][2] = {0, 1, 0, -1, 1, 0, -1, 0};
void init() {
memset(hh, -1, sizeof(hh));
tot = 0;
}
void add(int u, int v, int w) {
edge[tot].u = u; edge[tot].v = v;
edge[tot].w = w; edge[tot].next = hh[u];
hh[u] = tot ++;
}
int test(int x, int y ) {
if(x < 1 || x > n || y < 1 || y > m) return 0;
return 1;
}
int bfs() {
queue<int > Q;
memset(lev, -1, sizeof(lev));
Q.push(S); lev[S] = 0;
while(!Q.empty()) {
int u = Q.front(); Q.pop();
for(int i = hh[u]; i != -1; i = edge[i].next) {
int v = edge[i].v;
if(edge[i].w && lev[v] == -1) {
lev[v] = lev[u] + 1;
Q.push(v);
}
}
}
return lev[T] != -1;
}
int dfs(int u, int flow) {
if(u == T) return flow;
int tmp = flow, ad;
for(int i = hh[u]; i != -1; i = edge[i].next) {
int v = edge[i].v;
if(lev[v] == lev[u] + 1 && tmp > 0 && edge[i].w) {
ad = dfs(v, min(tmp, edge[i].w));
if(!tmp) break;
edge[i].w -= ad;
edge[i^1].w += ad;
tmp -= ad;
}
}
if(ad == 0) lev[u] = -1;
return flow- tmp;
}
int dinic() {
int ret = 0, tmp;
while(bfs()) while(tmp = dfs(S, INF)) ret += tmp;
return ret;
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("in.txt", "r", stdin);
#endif // ONLINE_JUDGE
scanf("%d", &t);
while(t --) {
scanf("%d%d", &n, &m);
int id = 0, sum = 0;
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= m; j++) {
++ id;
scanf("%d", &num[i][j]);
sum += num[i][j];
ids[i][j] = id;
}
}
init();
S = 0; T = n*m + 2;
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= m; j++) {
int mark = (i+j) & 1;
id = ids[i][j];
if(mark) {
add(id, T, num[i][j]); add(T, id, 0);
}
else {
add(S, id, num[i][j]); add(id, S, 0);
for(int k = 0; k < 4; k++) {
int nx = i + dir[k][0];
int ny = j + dir[k][1];
int tmp = test(nx, ny);
if(!tmp) continue;
add(id, ids[nx][ny], INF); add(ids[nx][ny], id, 0);
}
}
}
}
int ans = dinic();
printf("%d\n", sum - ans);
}
return 0;
}