您的位置:首页 > 其它

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

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

分析: 如果把每个无线网络所在城市与其覆盖的另一个城市之间视作一条路径的话, 那么所有的城市都只能属于其中一条路径,  所以题目可以理解为, 存在多少条路径可以覆盖掉所有的城市, 即最小路径覆盖数.   又 最小路径覆盖数 = 总点数|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
4000
][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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: