BZOJ 2150. 部落战争(最小路径覆盖问题)BZOJ千题计划

Posted 繁凡さん

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了BZOJ 2150. 部落战争(最小路径覆盖问题)BZOJ千题计划相关的知识,希望对你有一定的参考价值。

整理的算法模板合集: ACM模板

点我看算法全家桶系列!!!

实际上是一个全新的精炼模板整合计划


刷题就图一乐

题目链接

https://hydro.ac/d/bzoj/p/2150

hydro 的 BZOJ 修复工程

题目描述

lanzerb 的部落在 A 国的上部,他们不满天寒地冻的环境,于是准备向 A 国的下部征战来获得更大的领土。A 国是一个 m×nm\\times nm×n 的矩阵,其中某些地方是城镇,某些地方是高山深涧无人居住。lanzerb 把自己的部落分成若干支军队,他们约定:

  1. 每支军队可以从任意一个城镇出发,并只能从上往向下征战,不能回头。途中只能经过城镇,不能经过高山深涧。
  2. 如果某个城镇被某支军队到过,则其他军队不能再去那个城镇了。
  3. 每支军队都可以在任意一个城镇停止征战。
  4. 所有军队都很奇怪,他们走的方法有点像国际象棋中的马。不过马每次只能走 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

3 3 1 2
...
.x.
...

样例输出 #1

4

样例输入 #2

5 4 1 1
....
..x.
...x
....
x...

样例输出 #2

5

数据规模与约定

对于 100%100\\%100% 的数据,1≤m,n≤501\\leq m,n\\leq 501m,n501≤r,c≤101\\leq r,c\\leq 101r,c10

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 91 个结点,建图之后二分图最大匹配为 4 4 4,共有 8 − 4 = 4 8-4=4 84=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千题计划的主要内容,如果未能解决你的问题,请参考以下文章

最小路径覆盖BZOJ2150-部落战争

bzoj2150: 部落战争(匈牙利)

bzoj2150: 部落战争

bzoj2150部落战争 有上下界最小流

[Bzoj ]2150 部落战争

部落战争(建图+最小路径覆盖)