POJ 3020(二分图之最小路径覆盖)

题目链接:http://poj.org/problem?id=3020

题意:给出n行m列字符,‘*’表示城市,‘o’表示空地,在城市建立无线覆盖,每个可以覆盖到相邻的城市,并且已被覆盖的城市不能被再次覆盖,求能覆盖所有的点的最少的边。

思路:

这题是最小路径覆盖问题,利用一个定理:
最小路径覆盖=顶点数-最大匹配数

题目的难点应该在于如何建图,建立二分图的模型。

先给每个城市编号,以城市作为点集,建立二分图的模型。

具体实现代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
using namespace std;

const int INF=0x3f3f3f3f;
const int maxn=505;
int T,n,m;
int map[maxn][maxn];
int line[maxn][maxn];
int girl[maxn];
bool used[maxn];
int cnt;

int dx[]={-1,1,0,0};
int dy[]={0,0,-1,1};

bool dfs(int x){
	for(int i=1;i<=cnt;i++){
		if(line[x][i]&&!used[i]){
			used[i]=1;
			if(!girl[i]||dfs(girl[i])){
				girl[i]=x;
				return true;
			}
		}
	}
	return false;
}

int hungary(){
	int ans=0;
	memset(girl,0,sizeof(girl));
	for(int i=1;i<=cnt;i++){
		memset(used,0,sizeof(used));
		if(dfs(i))
			ans++;
	}
	return ans;
}

int main(){
#ifndef ONLINE_JUDGE
	freopen("test.in","r",stdin);
	freopen("test.out","w",stdout);
#endif
	scanf("%d",&T);
	while(T--){
		scanf("%d%d",&n,&m);
		memset(line,0,sizeof(line));
		memset(map,0,sizeof(map));
		char tmp[100];
		cnt=1;
		for(int i=1;i<=n;i++){
			scanf("%s",tmp);
			for(int j=0;j<m;j++){
				if(tmp[j]=='*')
					map[i][j+1]=cnt++;
			}
		}
		for(int i=1;i<=n;i++){
			for(int j=1;j<=m;j++){
				if(map[i][j]){
					for(int k=0;k<4;k++){
						int x=i+dx[k];
						int y=j+dy[k];
						if(map[x][y]){
							line[map[i][j]][map[x][y]]=1;
						}
					}
				}
			}
		}
		cnt--;
		int ans=cnt-hungary()/2;
		printf("%d\n",ans);
	}
	return 0;
}


你可能感兴趣的:(POJ 3020(二分图之最小路径覆盖))