poj 3020 Antenna Placement (最小路径覆盖, 匈牙利算法, 拆点形成二分图)

题意:  给出网格, 每个点有两种可能: 空地或者城市.   现在要建立无线网络区, 每个无线网络区只能放在城市中,  并且只能朝东南西北某一个方向覆盖另外一个城市(必须相邻), 求最少要建立几个无线网络区才能覆盖所有的城市.

分析: 如果把每个无线网络所在城市与其覆盖的另一个城市之间视作一条路径的话, 那么所有的城市都只能属于其中一条路径,  所以题目可以理解为, 存在多少条路径可以覆盖掉所有的城市, 即最小路径覆盖数.   又 最小路径覆盖数 = 总点数|P| - 最大匹配数. 所以可以通过将所有城市之间的相邻关系建立成一张图, 再求最大匹配数(可采用匈牙利算法).

建图:  题目给的是以(*,o)组成的网格.可以将*所在位置以数字进行编号, 如

oo**oo

*o*oo*

***o*o

可编号为:

001200

304005

678090

由于每个点都可能作为无线网络区去对相邻点覆盖即存在1->2和2->1的情况, 说明该图可为无向图.

要求最大匹配数就得将图拆成二分图.   我们可以采用拆点的方法, 即一个点拆成两个点, 分别置于V集合和V'集合,这样就可以形成二分图, 点集两边点数相等.

(注意:此时求出的最大匹配数为原图的2倍, 原因是:   假如V集中点1与V'集中点2'相匹配, 则由于拆点的关系, 必定存在V集中点2与V'集中点1'相匹配.故最后计算的时候应该除以2).


最小路径覆盖数 = 总点数|P|- 最大匹配数 .  证明参考百度百科:  最小路径覆盖公式证明

#include<iostream>
#include<cstring>
#include<string>
#include<algorithm>
#include<queue>
#include<stack>
#include<cstdio>
using namespace std;
int n, m, len, ans;
int Map[550][550];
int edge[550][550];
int d[4][2]={{-1, 0}, {0,-1}, {1, 0}, {0, 1}};
int flag[550], link[550];
bool dfs(int x){
	int y;
	for(y=1; y<=len; y++){
		if(edge[x][y] && !flag[y]){
			flag[y]=1;
			if(link[y]==0 || dfs(link[y])){
				link[y] = x;
				return true;
			}
		}
	}
	return false;
}
int main(){
	int T, i, j, k;
	scanf("%d", &T);
	while(T--){
		//注意初始化
		memset(link, 0, sizeof(link));
		memset(Map, 0, sizeof(Map));
		memset(edge, 0, sizeof(edge));
		scanf("%d %d%*c", &n, &m);
		char c;
		len=0;
		for(i=1; i<=n; i++){
			for(j=1; j<=m; j++){
				scanf("%c", &c);
				if(c=='*'){
					Map[i][j] = ++len;//给每个城市编号, Map存每个城市的编号
				}
			}
			getchar();
		}
		//建图  ->  拆点
		int x, y;
		for(i=1; i<=n; i++){
			for(j=1; j<=m; j++){
				if(Map[i][j]){
					int id = Map[i][j];
					for(k=0; k<4; k++){//东南西北连边中~~~
						x = i+d[k][0];
						y = j+d[k][1];
						int idd = Map[x][y];
						if(Map[x][y]) edge[id][idd]=1;
					}
				}
			}
		}
		ans=0;
		for(i=1;i<=len; i++){
			memset(flag, 0, sizeof(flag));
			if(dfs(i)) ans++;
		}
		//len是总点数,  ans是上图求出的最大匹配数, 应当除以2才是原集合的最大匹配数
		printf("%d\n", len-ans/2);
	}
	return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值