Description :
一块n*m的草坪,有两种高度的草,#表示较高的草,.表示较矮的草,现在要从左往右和从上往下用收割机收割,在相同高度的草坪上收割机不耗油,当高度变化时需要耗花费为a的油,还可以花费b改变任一块草坪的高度,问这n+m排收割机扫过这块草坪需要的最小花费
Solution:
经典二元最小割模型。源点连向较矮的草,容量为B;较高的草连向回店,容量为B;高度不同的草之间互相连容量为A的边
跑最小割得出答案
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <queue>
using namespace std;
const int N = 5005, inf = 1e9;
const int dx[] = {-1, 1, 0, 0}, dy[] = {0, 0, -1, 1};
struct edge {
int nxt, to, f;
} e[2000005];
int n, m, cnt = 1, source, sink, A, B;
int h[N], iter[N], d[N], b[55][55];
char s[55];
#define id(i, j) (i - 1) * m + j
void link(int u, int v, int f) {
e[++cnt].nxt = h[u];
h[u] = cnt;
e[cnt].to = v;
e[cnt].f = f;
}
void insert(int u, int v, int f) {
link(u, v, f);
link(v, u, 0);
}
bool bfs() {
queue<int> q;
q.push(source);
memset(d, -1, sizeof(d));
d[source] = 0;
while(!q.empty()) {
int u = q.front();
q.pop();
for(int i = h[u]; i; i = e[i].nxt) {
if(d[e[i].to] == -1 && e[i].f) {
d[e[i].to] = d[u] + 1;
q.push(e[i].to);
}
}
}
return d[sink] != -1;
}
int dfs(int u, int delta) {
if(u == sink) {
return delta;
}
int ret = 0;
for(int &i = iter[u]; i; i = e[i].nxt) {
if(d[e[i].to] == d[u] + 1 && e[i].f) {
int x = dfs(e[i].to, min(delta, e[i].f));
e[i].f -= x;
e[i ^ 1].f += x;
delta -= x;
ret += x;
if(!delta) {
break;
}
}
}
return ret;
}
int dinic() {
int ret = 0;
while(bfs()) {
for(int i = source; i <= sink; ++i) {
iter[i] = h[i];
}
ret += dfs(source, inf);
}
return ret;
}
int main() {
scanf("%d%d%d%d", &n, &m, &A, &B);
sink = n * m + 1;
for(int i = 1; i <= n; ++i) {
scanf("%s", s + 1);
for(int j = 1; j <= m; ++j) {
if(s[j] == '.') {
insert(source, id(i, j), B);
} else {
insert(id(i, j), sink, B);
}
for(int k = 0; k < 4; ++k) {
int x = i + dx[k], y = j + dy[k];
if(x && x <= n && y && y <= m) {
insert(id(i, j), id(x, y), A);
}
}
}
}
printf("%d\n", dinic());
return 0;
}