BZOJ2132 圈地計劃 【最小割】
題目
最近房地產商GDOI(Group of Dumbbells Or Idiots)從NOI(Nuts Old Idiots)手中得到了一塊開發土地。據了解,
這塊土地是一塊矩形的區域,可以縱橫劃分為N×M塊小區域。GDOI要求將這些區域分為商業區和工業區來開發。根
據不同的地形環境,每塊小區域建造商業區和工業區能取得不同的經濟價值。更具體點,對於第i行第j列的區域,
建造商業區將得到Aij收益,建造工業區將得到Bij收益。另外不同的區域連在一起可以得到額外的收益,即如果區
域(I,j)相鄰(相鄰是指兩個格子有公共邊)有K塊(顯然K不超過4)類型不同於(I,j)的區域,則這塊區域能增加k
×Cij收益。經過Tiger.S教授的勘察,收益矩陣A,B,C都已經知道了。你能幫GDOI求出一個收益最大的方案麽?
輸入格式
輸入第一行為兩個整數,分別為正整數N和M,分別表示區域的行數和列數;
第2到N+1列,每行M個整數,表示商業區收益矩陣A;
第N+2到2N+1列,每行M個整數,表示工業區收益矩陣B;
第2N+2到3N+1行,每行M個整數,表示相鄰額外收益矩陣C。
任何數字不超過1000”的限制
輸出格式
輸出只有一行,包含一個整數,為最大收益值。
輸入樣例
3 3
1 2 3
4 5 6
7 8 9
9 8 7
6 5 4
3 2 1
1 1 1
1 3 1
1 1 1
輸出樣例
81
提示
【數據規模】
對於100%的數據有N,M≤100
題解
類似BZOJ2127happiness
上一次似乎沒有寫博,,
一個經典的最小割模型,每個人有兩個選擇,每個選擇有不同收益,當一些人選擇相同時會有額外的收益,求最大收益
用一個這樣的圖:
這個圖有兩種割法
設二人同選\(A\)的額外收益為\(w_a\),同選\(B\)為\(w_b\)
①當二者選擇不同時,除了沒選的收益外,會付出額外代價\(w_a + w_b\)
對應的圖中代價,要割掉不同側的邊,以及中間的一條邊
\[x_1 + x_3 + x_6\]
\[x_2 + x_4 + x_5\]
②選擇相同時,除了
對應圖中,割掉一側的邊即可
\[x_1 + x_5\]
\[x_2 + x_6\]
那麽有:
\[x_1 + x_3 + x_6 = w_a + w_b\]
\[x_2 + x_4 + x_5 = w_a + w_b\]
\[x_1 + x_5 = w_b\]
\[x_2 + x_6 = w_a\]
似乎有多解,我們不妨設:
\[x_1 = x_5\]
\[x_2 = x_6\]
即可得到一組比較特殊的解:
\[x_1 = x_5 = \frac{w_b}{2} \qquad x_2 = x_6 = \frac{w_a}{2} \qquad x_3 = x_4 = \frac{w_a + w_b}{2}\]
那麽最小割即為在提前擁有所有收益後必須付出的最小代價了
具體建圖中,我們通常將邊權乘\(2\)變為整數
回到這題,建圖就很裸了
不過這題是不同產生收益,我們二分染色一下,然後對於其中一種顏色的點\(AB\)交換即可
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#define LL long long int
#define Redge(u) for (int k = h[u],to; k; k = ed[k].nxt)
#define REP(i,n) for (int i = 1; i <= (n); i++)
#define BUG(s,n) for (int i = 1; i <= (n); i++) cout<<s[i]<<‘ ‘; puts("");
using namespace std;
const int maxn = 10005,maxm = 200005,N = 105,INF = 1000000000;
inline int read(){
int out = 0,flag = 1; char c = getchar();
while (c < 48 || c > 57){if (c == ‘-‘) flag = -1; c = getchar();}
while (c >= 48 && c <= 57){out = (out << 3) + (out << 1) + c - 48; c = getchar();}
return out * flag;
}
int A[N][N],B[N][N],C[N][N],id[N][N],X[4] = {0,0,-1,1},Y[4] = {-1,1,0,0};
int n,m,S,T;
int h[maxn],ne = 2;
struct EDGE{int to,nxt,f;}ed[maxm];
inline void build(int u,int v,int w){
ed[ne] = (EDGE){v,h[u],w}; h[u] = ne++;
ed[ne] = (EDGE){u,h[v],0}; h[v] = ne++;
}
int d[maxn],vis[maxn],used[maxn],cur[maxn],now;
int q[maxn],head,tail;
inline bool bfs(){
q[head = tail = 1] = S; vis[S] = now; d[S] = 0;
int u;
while (head <= tail){
u = q[head++];
Redge(u) if (ed[k].f && vis[to = ed[k].to] != now){
d[to] = d[u] + 1; vis[to] = now;
if (to == T) return true;
q[++tail] = to;
}
}
return vis[T] == now;
}
int dfs(int u,int minf){
if (u == T || !minf) return minf;
int flow = 0,f,to;
if (used[u] != now) cur[u] = h[u],used[u] = now;
for (int& k = cur[u]; k; k = ed[k].nxt)
if (vis[to = ed[k].to] == now && d[to] == d[u] + 1 && (f = dfs(to,min(minf,ed[k].f)))){
ed[k].f -= f; ed[k ^ 1].f += f;
flow += f; minf -= f;
if (!minf) break;
}
return flow;
}
int maxflow(){
int flow = 0; now = 1;
while (bfs()){
flow += dfs(S,INF);
now++;
}
return flow;
}
int main(){
n = read(); m = read(); S = 0; T = m * n + 1;
int ans = 0;
REP(i,n) REP(j,m) A[i][j] = read(),ans += A[i][j],A[i][j] <<= 1;
REP(i,n) REP(j,m) B[i][j] = read(),ans += B[i][j],B[i][j] <<= 1;
REP(i,n) REP(j,m) C[i][j] = read();
REP(i,n) REP(j,m) id[i][j] = (i - 1) * m + j;
REP(i,n) REP(j,m){
int x,y,tmp;
if ((i & 1) ^ (j & 1)){
for (int k = 0; k < 4; k++){
x = i + X[k];
y = j + Y[k];
if (x < 1 || y < 1 || x > n || y > m) continue;
tmp = C[i][j] + C[x][y]; ans += tmp << 1;
A[i][j] += tmp; B[i][j] += tmp;
A[x][y] += tmp; B[x][y] += tmp;
build(id[i][j],id[x][y],tmp << 1);
build(id[x][y],id[i][j],tmp << 1);
}
}
}
REP(i,n) REP(j,m){
if ((i & 1) ^ (j & 1)){
build(S,id[i][j],A[i][j]);
build(id[i][j],T,B[i][j]);
}
else {
build(S,id[i][j],B[i][j]);
build(id[i][j],T,A[i][j]);
}
}
printf("%d\n",ans - (maxflow() >> 1));
return 0;
}
BZOJ2132 圈地計劃 【最小割】