题目大意
给一个 h * w 的矩阵,'#' 表示草地,'.' 表示洞,草地变成洞花费d,洞变成草地花费f,在洞和草地的边缘间围栏花费b,求最小花费。
要求矩阵边缘必须是草地。
思路
矩阵中的每个单元为一个节点,节点分为两个集合S集和T集,最终与s相连的S集节点为草地,T集节点为洞。
s向原为草地的节点建容量为d的边,当此边进入最小割时,此节点进入T集,即草地变为洞,边缘草地与s建立容量未INF的边即此边不能进入割集。
同理原为洞的节点和t点建立容量为f的边。
在矩阵中相邻的节点之间建立双向的容量都为b的边,当最终相邻节点分别在S集和T集时,此边进入割集。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <vector>
#include <queue>
#define INF 0x3f3f3f3f
#define rep0(i, n) for (int i = 0; i < n; i++)
#define rep1(i, n) for (int i = 1; i <= n; i++)
#define rep_0(i, n) for (int i = n - 1; i >= 0; i--)
#define rep_1(i, n) for (int i = n; i > 0; i--)
#define MAX(x, y) (((x) > (y)) ? (x) : (y))
#define MIN(x, y) (((x) < (y)) ? (x) : (y))
#define mem(x, y) memset(x, y, sizeof(x))
#define MAXW 100
#define idx(x, y) ((x) * 50 + (y))
using namespace std;
typedef long long ll;
const int T = MAXW * MAXW - 1;
struct Edge
{
int to, cap, flow, rev;
};
vector<Edge> G[MAXW * MAXW];
void addEdge(int u, int v, int cap)
{
G[u].push_back((Edge){v, cap, 0, G[v].size()});
G[v].push_back((Edge){u, 0, 0, G[u].size() - 1});
}
int d, f, b;
int w, h;
char g[MAXW][MAXW];
int level[MAXW * MAXW], itr[MAXW * MAXW];
void bfs()
{
mem(level, -1);
level[0] = 0;
queue<int> q;
q.push(0);
while (!q.empty())
{
int u = q.front();
q.pop();
for (int i = 0; i < G[u].size(); i++)
{
Edge e = G[u][i];
if (level[e.to] < 0 && e.cap > e.flow)
{
q.push(e.to);
level[e.to] = level[u] + 1;
}
}
}
}
int dfs(int u, int f)
{
if (u == T)
return f;
for (int& i = itr[u]; i < G[u].size(); i++)
{
Edge& e = G[u][i];
if (level[e.to] > level[u] && e.cap > e.flow)
{
int d = dfs(e.to, MIN(f, e.cap - e.flow));
if (d)
{
e.flow += d;
G[e.to][e.rev].flow -= d;
return d;
}
}
}
return 0;
}
ll max_flow()
{
ll flow = 0;
while (true)
{
bfs();
if (level[T] < 0)
return flow;
ll f;
mem(itr, 0);
while ((f = dfs(0, INF)) > 0)
{
flow += f;
}
}
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("in.txt", "r", stdin);
#endif // ONLINE_JUDGE
int t;
scanf("%d", &t);
while (t--)
{
scanf("%d %d", &w, &h);
scanf("%d %d %d", &d, &f, &b);
for (int i = 0; i < h; i++)
{
scanf("%s", g[i]);
}
for (int i = 0; i < h; i++)
for (int j = 0; j < w; j++)
G[i * 50 + j].clear();
ll ans = 0;
for (int i = 0; i < w; i++)
if (g[0][i] == '.')
ans += f;
for (int i = 0; i < w; i++)
if (g[h - 1][i] == '.')
ans += f;
for (int i = 1; i < h - 1; i++)
if (g[i][0] == '.')
ans += f;
for (int i = 1; i < h - 1; i++)
if (g[i][w - 1] == '.')
ans += f;
if (w == 2 || h == 2)
{
printf("%lld\n", ans);
continue;
}
for (int i = 1; i < w - 1; i++)
{
addEdge(0, idx(0, i), INF);
addEdge(0, idx(h - 1, i), INF);
addEdge(idx(h - 1, i), idx(h - 2, i), b);
}
for (int i = 1; i < h - 1; i++)
{
addEdge(0, idx(i, 0), INF);
addEdge(0, idx(i, w - 1), INF);
addEdge(idx(i, w - 1), idx(i, w - 2), b);
}
for (int i = 1; i < h - 1; i++)
{
for (int j = 1; j < w - 1; j++)
{
addEdge(idx(i - 1, j), idx(i, j), b);
addEdge(idx(i, j), idx(i - 1, j), b);
addEdge(idx(i, j - 1), idx(i, j), b);
addEdge(idx(i, j), idx(i, j - 1), b);
if (g[i][j] == '#')
{
addEdge(0, idx(i, j), d);
}
else
{
addEdge(idx(i, j), T, f);
}
}
}
ans += max_flow();
printf("%lld\n", ans);
}
return 0;
}