题面
我们可以将'.'抽象为一个可以通过的点, 将'x'抽象为一个不可通过的点.
那么题意便可以转化为: 一条路径可以看做从任意一个没有到达过的可通过的点出发到任意一个其他的可以通过却没有被到达过的点的一条路径, 要使每个点都被经过, 并且每个点都只能被经过一次, 这不是网络流中的最小路径覆盖吗, 大家可以去看一下魔术球问题, 魔术球问题就是最小路径覆盖的板子题.
对于这种题目, 我们将图中的每一个点拆为两个点<i, a>, <i, b>, 从源点向<i, a>连边, 从<i, b>向汇点连边, 若可从点\(i\)到达点\(j\), 就从点<i, a>向点<j, b>连一条边, 由于每个点都只能出现一次, 所以这些边的容量都是1, 然后跑一遍最大流即可, 这里还要注意一下的是, 军队只能向下走, 所以八个方向变为了四个方向, 分别是(c, r), (c, -r), (r, c), (r, -c).
跑完了最大流之后, 答案怎样统计呢, 我们最初每个可通过的点都是一条边, 所以最初有'.'的点数这么多条边, 我们不妨设这个数为\(sum\), 那么每次从点\(i\)向点\(j\)连边, 其实就意味着将两点之间的路径合并, 那么就会减少一条边, 我们又知道一个定理:最小点(在这个题中是路径(覆盖 = 点数 - 最大独立集 = 点数 - 最小割 = 点数 - 最大流, 所以最后的答案便是\(sum\) - \(dinic()\)的值, \(dinic()\)是一个函数, 他返回的是最大流的流量.
接下来还是看代码吧, 感觉解析应该没什么人会看...
具体代码
#include <iostream>
#include <cstring>
#include <cstdio>
#include <queue>
#define INF 1e9
using namespace std;
int m, n, R, C, mapp[105][105], u[4], v[4], S, T, head[100005], cnt = 1, cur[100005], d[100005], vis[100005], ans, sum;
struct node
{
int to, flow, next;
} edge[1000005];
inline int read()
{
int x = 0, w = 1;
char c = getchar();
while(c < '0' || c > '9') { if (c == '-') w = -1; c = getchar(); }
while(c >= '0' && c <= '9') { x = x * 10 + c - '0'; c = getchar(); }
return x * w;
}
inline void add(int u, int v, int w)
{
edge[++cnt] = { v, w, head[u] }; head[u] = cnt;
edge[++cnt] = { u, 0, head[v] }; head[v] = cnt;
}
bool bfs()
{
memset(d, 0, sizeof(d)); d[S] = 1;
queue<int> q; q.push(S);
while(!q.empty())
{
int u = q.front(); q.pop();
for(int i = head[u]; i; i = edge[i].next)
{
int v = edge[i].to;
if(!d[v] && edge[i].flow > 0) { d[v] = d[u] + 1; q.push(v); }
}
}
return d[T];
}
int dfs(int u, int a)
{
if(u == T || !a) return a;
int flow = 0;
for(int &i = cur[u]; i; i = edge[i].next)
{
int v = edge[i].to;
if(d[v] == d[u] + 1 && edge[i].flow > 0)
{
int f = dfs(v, min(a, edge[i].flow));
a -= f; flow += f; edge[i].flow -= f; edge[i ^ 1].flow += f;
}
if(!a) break;
}
if(a) d[u] = -1;
return flow;
}
int dinic()
{
int flow = 0;
while(bfs())
{
memcpy(cur, head, sizeof(head));
flow += dfs(S, INF);
}
return flow;
}
int main()
{
m = read(); n = read(); R = read(); C = read();
for(int i = 1; i <= m; i++)
for(int j = 1; j <= n; j++)
{
char c; cin>>c;
mapp[i][j] = (c == 'x');
if(mapp[i][j] == 1) sum++;
}
u[0] = C; u[1] = C; u[2] = R; u[3] = R; v[0] = R; v[1] = -R; v[2] = -C; v[3] = C;
S = 2 * m * n + 1; T = S + 1;
for(int i = 1; i <= m; i++)
for(int j = 1; j <= n; j++)
{
add(S, (i - 1) * n + j, 1);
add((i - 1) * n + j + m * n, T, 1);
if(!mapp[i][j])
for(int k = 0; k < 4; k++)
{
int x = i + u[k], y = j + v[k];
if(x >= 1 && x <= m && y >= 1 && y <= n && !mapp[x][y]) add((i - 1) * n + j, (x - 1) * n + y + m * n, 1);
}
}
printf("%d\n", m * n - sum - dinic());
return 0;
}
话说为啥我自己的博客二级标题总是显示不出来呢