BZOJ 2150: 部落战争 二分图最小路径覆盖

本文介绍了一个基于有向无环图的最小不相交路径覆盖问题的解决方案,通过拆点和最大匹配算法,解决如何用最少数量的军队统一全国的问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

2150: 部落战争

Time Limit: 10 Sec  Memory Limit: 259 MB
Submit: 1004  Solved: 564
[Submit][Status][Discuss]

Description

lanzerb的部落在A国的上部,他们不满天寒地冻的环境,于是准备向A国的下部征战来获得更大的领土。 A国是一个M*N的矩阵,其中某些地方是城镇,某些地方是高山深涧无人居住。lanzerb把自己的部落分成若干支军队,他们约定: 1. 每支军队可以从任意一个城镇出发,并只能从上往向下征战,不能回头。途中只能经过城镇,不能经过高山深涧。 2. 如果某个城镇被某支军队到过,则其他军队不能再去那个城镇了。 3. 每支军队都可以在任意一个城镇停止征战。 4. 所有军队都很奇怪,他们走的方法有点像国际象棋中的马。不过马每次只能走1*2的路线,而他们只能走R*C的路线。 lanzerb的野心使得他的目标是统一全国,但是兵力的限制使得他们在配备人手时力不从心。假设他们每支军队都能顺利占领这支军队经过的所有城镇,请你帮lanzerb算算至少要多少支军队才能完成统一全国的大业。

Input

第一行包含4个整数M、N、R、C,意义见问题描述。接下来M行每行一个长度为N的字符串。如果某个字符是'.',表示这个地方是城镇;如果这个字符时'x',表示这个地方是高山深涧。

Output

输出一个整数,表示最少的军队个数。

Sample Input

【样例输入一】
3 3 1 2
...
.x.
...
【样例输入二】
5 4 1 1
....
..x.
...x
....
x...

Sample Output

【样例输出一】
4

【样例输出二】
5
【样例说明】

【数据范围】
100%的数据中,1<=M,N<=50,1<=R,C<=10。


裸的有向无环图最小不相交路径覆盖,直接拆点,然后原图点数-最大匹配就可以。

最坏情况下覆盖全图需要n次,每次只覆盖一个点,现在最大匹配中两个点i,j匹配,代表有两个点可以缩成一

点,就是覆盖了i之后可以直接接着覆盖j,,所以答案-1。


#include<cmath>
#include<ctime>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<complex>
#include<iostream>
#include<algorithm>
#include<iomanip>
#include<vector>
#include<bitset>
#include<string>
#include<queue>
#include<set>
#include<map>
using namespace std;
inline int read()
{
	int x=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch<='9'&&ch>='0'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
	return x*f;
}
const int N=2500;
int n,m,ecnt,num[60][60],xx[4],yy[4],last[N],vis[N],match[N],clr;
struct EDGE{int to,nt;}e[N<<3];
inline void add(int u,int v)
{e[++ecnt]=(EDGE){v,last[u]};last[u]=ecnt;}
bool hungary(int u)
{
	for(int i=last[u];i;i=e[i].nt)
	{
		if(clr==vis[e[i].to])continue;
		vis[e[i].to]=clr;
		if(!match[e[i].to]||hungary(match[e[i].to]))
		{
			match[e[i].to]=u;
			return 1;
		}
	}
	return 0;
}
int main()
{
	n=read();m=read();int r=read(),c=read();
	char ch[60];int cnt=0;
	for(int i=1;i<=n;i++)
	{
		scanf("%s",ch);
		for(int j=1;j<=m;j++)
		{
			if(ch[j-1]=='.')num[i][j]=++cnt;
			
		}
	}
	xx[0]=r,xx[1]=r,xx[2]=c,xx[3]=c;
	yy[0]=c,yy[1]=-c,yy[2]=-r,yy[3]=r;
	for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)
	{
		if(!num[i][j])continue;
		for(int k=0;k<4;k++)
		{
			int nx=i+xx[k],ny=j+yy[k];
			if(nx<1||ny<1||nx>n||ny>m||!num[nx][ny])continue;
			add(num[i][j],num[nx][ny]);
		}
	}
	int ans=0;
	for(int i=1;i<=cnt;i++){clr++;if(hungary(i))ans++;}
	printf("%d\n",cnt-ans);
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值