51nod_2929 部落战争(DAG最小路径覆盖)

部落战争

Problem Description

lanzerb的部落在A国的上部,他们不满天寒地冻的环境,于是准备向A国的下部征战来获得更大的领土。

A国是一个M*N的矩阵,其中某些地方是城镇,某些地方是高山深涧无人居住。lanzerb把自己的部落分成若干支军队,他们约定:

每支军队可以从任意一个城镇出发,并只能从上往向下征战,不能回头。途中只能经过城镇,不能经过高山深涧。

如果某个城镇被某支军队到过,则其他军队不能再去那个城镇了。

每支军队都可以在任意一个城镇停止征战。

所有军队都很奇怪,他们走的方法有点像国际象棋中的马。不过马每次只能走12的路线,而他们只能走RC的路线。

lanzerb的野心使得他的目标是统一全国,但是兵力的限制使得他们在配备人手时力不从心。假设他们每支军队都能顺利占领这支军队经过的所有城镇,请你帮lanzerb算算至少要多少支军队才能完成统一全国的大业。

Input

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

Output

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

Sample Input

输入样例1:
3 3 1 2

.x.

输入样例2:
5 4 1 1

…x.
…x

x…
输入样例3:
1 1 1 1
.

Sample Output

输出样例1:
4
输出样例2:
5
输出样例3:
1

题解:

将每个点重新编号,若 ( i , j ) (i,j) (i,j)能转移到点 ( x , y ) (x,y) (x,y),则连边。
所有点编号并连边后,可得到一个DAG图。
初始点任意,求每个点最多允许一次停留=>DAG不相交的最小路径覆盖。
将DAG图中的,每个点拆分为两个点,对于边 i − > j i->j i>j,连边 i i − > j 2 i_i->j_2 ii>j2
新图为二分图,求其最大匹配。点的总数-最大匹配 即为所求。

#include<stdio.h>
#include<iostream>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<map>
#include<vector>
#include<queue>
#include<iterator>
#define dbg(x) cout<<#x<<" = "<<x<<endl;
#define INF 0x3f3f3f3f
#define eps 1e-6
 
using namespace std;
typedef long long LL;   
typedef pair<int, int> P;
const int maxn = 5020;
const int mod = 1000000007;
int id[52][52], dx[4], dy[4], vis[maxn], visy[maxn], mat[maxn];
char str[52][52];
vector<int> g[maxn];
bool Find(int u);
int match(int n, int m);

int main()
{
    int n, m, i, j, k, cnt=0, r, c;
	scanf("%d %d %d %d", &n, &m, &r, &c);
	dx[0] = dx[1] = r, dx[2] = dx[3] = c;
	dy[0] = c, dy[1] = -c, dy[2] = r, dy[3] = -r;
	for(i=0;i<n;i++)
		scanf(" %s", str[i]);
	for(i=0;i<n;i++)
		for(j=0;j<m;j++)
			if(str[i][j] == '.')id[i][j] = ++cnt;
	for(i=0;i<n;i++)
		for(j=0;j<m;j++)
		if(str[i][j] == '.')
			for(k=0;k<4;k++){
				int nx = i+dx[k], ny = j+dy[k];
				if(nx>=0 && nx<n && ny>=0 && ny<m && str[nx][ny] == '.')
					g[id[i][j]].push_back(id[nx][ny]);
			}
	
	int ans;
	ans = match(cnt, cnt);
	printf("%d\n", cnt-ans);
    return 0;
}

int match(int n, int m)
{
	int ans = 0;
	memset(mat, 0, sizeof(mat));
	for(int i=1;i<=m;i++){
		memset(vis, 0, sizeof(vis));
		if(Find(i))ans++;
	}
	return ans;
}

bool Find(int u)
{
	for(int i=0;i<g[u].size();i++){
		int v = g[u][i];
		if(vis[v])continue;
		vis[v] = 1;
		if(!mat[v] || Find(mat[v])){
			mat[v] = u;
			return true;
		}
	}
	return false;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值