BZOJ 2150. 部落战争(最小路径覆盖问题)BZOJ千题计划
Posted 繁凡さん
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了BZOJ 2150. 部落战争(最小路径覆盖问题)BZOJ千题计划相关的知识,希望对你有一定的参考价值。
整理的算法模板合集: ACM模板
实际上是一个全新的精炼模板整合计划
刷题就图一乐
题目链接
https://hydro.ac/d/bzoj/p/2150
题目描述
lanzerb 的部落在 A 国的上部,他们不满天寒地冻的环境,于是准备向 A 国的下部征战来获得更大的领土。A 国是一个 m×nm\\times nm×n 的矩阵,其中某些地方是城镇,某些地方是高山深涧无人居住。lanzerb 把自己的部落分成若干支军队,他们约定:
- 每支军队可以从任意一个城镇出发,并只能从上往向下征战,不能回头。途中只能经过城镇,不能经过高山深涧。
- 如果某个城镇被某支军队到过,则其他军队不能再去那个城镇了。
- 每支军队都可以在任意一个城镇停止征战。
- 所有军队都很奇怪,他们走的方法有点像国际象棋中的马。不过马每次只能走 1×21\\times 21×2 的路线,而他们只能走 r×cr\\times cr×c 的路线。lanzerb 的野心使得他的目标是统一全国,但是兵力的限制使得他们在配备人手时力不从心。假设他们每支军队都能顺利占领这支军队经过的所有城镇,请你帮 lanzerb 算算至少要多少支军队才能完成统一全国的大业。
输入格式
第一行包含 444 个整数 m,n,r,cm,n,r,cm,n,r,c,意义见问题描述。接下来 mmm 行每行一个长度为 nnn 的字符串。如果某个字符是 .
,表示这个地方是城镇;如果这个字符是 x
,表示这个地方是高山深涧。
输出格式
输出一个整数,表示最少的军队个数。
样例输入 #1
样例输出 #1
样例输入 #2
样例输出 #2
数据规模与约定
对于 100%100\\%100% 的数据,1≤m,n≤501\\leq m,n\\leq 501≤m,n≤50,1≤r,c≤101\\leq r,c\\leq 101≤r,c≤10。
Solution
我们的军队从某一点出发,所有经过的结点都将被占领,显然问题就等价于在一张有向图上,从任意一个点出发,每个点只能访问一次,求访问所有结点至少要几条路径。
显然就是有向图的最小路径覆盖。
我们只需要构造二分图,将原图每个顶点 i i i 拆分成二分图 X , Y X,Y X,Y 集合中的两个顶点 X i X_i Xi和 Y i Y_i Yi。对于原图中存在的每条边 ( i , j ) (i,j) (i,j),在二分图中连接边 ( X i , Y j ) (X_i,Y_j) (Xi,Yj) 。将二分图最大匹配模型转化为网络流模型,求网络最大流。
最小路径覆盖的条数,就是原图顶点数,减去二分图最大匹配数。
对于样例一
...
.x.
...
我们共有 9 − 1 9-1 9−1 个结点,建图之后二分图最大匹配为 4 4 4,共有 8 − 4 = 4 8-4=4 8−4=4 条最小路劲覆盖。
可以理解为,若两个点连接之后,路径数显然减一。总结点数减去最大连边,也即最大匹配得到的就是最小路径覆盖。
Code
#include <bits/stdc++.h>
using namespace std;
#define node(i, j) ((i - 1) * m + j)
const int maxn = 4e5 + 7, maxm = 2e6 + 7, maxs = 100 + 7, INF = 0x3f3f3f3f;
using ll = long long;
int n, m, s, t, r, c;
ll ans;
int head[maxn], ver[maxm], edge[maxm], nex[maxm], tot;
bool vis[maxn];
int depth[maxn];
char ch[maxs][maxs];
int num;
int now[maxn];
queue <int> q;
void add(int x, int y, int z)
{
ver[tot] = y, edge[tot] = z, nex[tot] = head[x], head[x] = tot ++ ;
ver[tot] = x, edge[tot] = 0, nex[tot] = head[y], head[y] = tot ++ ;
}
bool bfs()
{
memset(depth, 0x3f, sizeof depth);
while(q.size())
q.pop();
q.push(s), depth[s] = 0;
now[s] = head[s];
while(q.size()) {
int x = q.front();
q.pop();
for (int i = head[x]; ~i; i = nex[i]) {
int y = ver[i];
ll z = edge[i];
if(z > 0 && depth[y] == INF) {
q.push(y);
now[y] = head[y];
depth[y] = depth[x] + 1;
if(y == t) return true;
}
}
}
return false;
}
ll dfs(int x, ll flow)
{
if(x == t || flow == 0) return flow;
ll ans = 0, k, i;
for (i = now[x]; ~i && flow; i = nex[i]) {
now[x] = i;
int y = ver[i];
ll z = edge[i];
if(z > 0 && (depth[y] == depth[x] + 1)) {
k = dfs(y, min(flow, z));
if(k == 0) depth[y] = INF;
edge[i] -= k;
edge[i ^ 1] += k;
ans += k;
flow -= k;
}
}
return ans;
}
ll dinic()
{
ll maxflow = 0;
while(bfs()) {
for (int i = 1; i <= maxn; ++ i)
now[i] = head[i];
while(int f = dfs(s, INF))
maxflow += f;
}
return maxflow;
}
bool check(int x, int y)
{
return x >= 1 && x <= n && y >= 1 && y <= m && ch[x][y] == '.';
}
int main()
{
memset(head, -1, sizeof head);
num = 0;
scanf("%d%d%d%d", &n, &m, &r, &c);
s = 0;
t = maxn - 1;
for (int i = 1; i <= n; ++ i)
for (int j = 1; j <= m; ++ j) {
cin >> ch[i][j];
if(ch[i][j] == '.') {
num ++ ;
add(s, node(i, j), 1);
add(node(i + n * m, j + n * m), t, 1);
}
}
for (int i = 1; i <= n; ++ i) {
for (int j = 1; j <= m; ++ j) {
if(ch[i][j] == 'x') continue;
if(check(i + r, j + c)) add(node(i, j), node(i + r + n * m, j + c + n * m), 1);
if(check(i + c, j + r)) add(node(i, j), node(i + c + n * m, j + r + n * m), 1);
if(check(i + c, j - r)) add(node(i, j), node(i + c + n * m, j - r + n * m), 1);
if(check(i + r, j - c)) add(node(i, j), node(i + r + n * m, j - c + n * m), 1);
}
}
cout << num - dinic() << endl;
return 0;
}
以上是关于BZOJ 2150. 部落战争(最小路径覆盖问题)BZOJ千题计划的主要内容,如果未能解决你的问题,请参考以下文章