版权声明:希望能帮到弱校的ACMer成长,因为自己是弱校菜鸡~~~~ https://blog.csdn.net/Mr__Charles/article/details/82628242
POJ T3020 Antenna Placement
题意:
鄙人英语菜的一批,一开始没搞懂题意,卡了挺久。
'*'是城市,'o'是空地,椭圆的天线覆盖范围要覆盖的是城市'*',而不是覆盖空地。一个矩形中,有N个城市’*’,现在这n个城市都要覆盖无线,若放置一个基站,那么它至多可以覆盖相邻的两个城市。问至少放置多少个基站才能使得所有的城市都覆盖无线?
题解:
做过裸题的话,其实只要想出如何建图,其他跟裸题是一样的。
本题,属于二分图的最小路径覆盖,其实又分有向图的最小路径覆盖和无向图的最小路径覆盖,有向图的最小路径覆盖需要标记用过的边,比较麻烦,所以本题就用无向图的最小路径覆盖的写法来写。
如何建图呢,咱们可以把每个‘*’编号,鄙人是从0开始的,例:
oo*o
oo**
**oo
可以转换成如下图:
-1 -1 0 -1
-1 -1 1 2
3 4 -1 -1
那么构成的边有
e01 e10 e12 e21 e34 e43
而对于U和V两个点集,两者的点数是一样的,相当于复制了点
图建好后,就可以开始裸题操作了
二分图技巧和模版
代码:
#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
int n,m,cnt,mov[4][2]={-1,0,0,1,1,0,0,-1};
char ch[11];
int maps[40][11],g[405];
bool line[405][405],used[405];
void init(){
cnt = 0;
memset(maps,-1,sizeof(maps));
memset(g,-1,sizeof(g));
memset(line,false,sizeof(line));
}
bool charge(int x,int y){
if(x < 0 || x > n - 1 || y < 0 || y > m-1 || maps[x][y] == -1)
return false;
return true;
}
void build(){
for(int i = 0; i < n; ++i){
for(int j = 0; j < m; ++j){
if(maps[i][j] != -1){
for(int k = 0; k < 4; ++k){
int x = i + mov[k][0];
int y = j + mov[k][1];
if(charge(x,y)){
line[maps[i][j]][maps[x][y]] = true;
}
}
}
}
}
}
bool find(int x){
for(int i = 0; i < cnt; ++i)
if(line[x][i] && !used[i]){
used[i] = true;
if(g[i] == -1 || find(g[i])){
g[i] = x;
return true;
}
}
return false;
}
int match(){
int sum = 0;
for(int i = 0;i < cnt; ++i){
memset(used,false,sizeof(used));
if(find(i)) sum++;
}
return sum;
}
int main(){
int t;
scanf("%d",&t);
while(t--){
scanf("%d%d",&n,&m);
init();
for(int i = 0; i < n; ++i){
scanf("%s",ch);
for(int j = 0; j < m; ++j){
if(ch[j] == '*')
maps[i][j] = cnt++;
}
}
build();
printf("%d\n",cnt - match() / 2);
}
return 0;
}