POJ3020 (Antenna Placement)[二分图匹配,匈牙利算法]

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

给定一个网格,网格中某些格子有点,用一些2*1的挡板覆盖,问在可以重叠覆盖的情况下覆盖掉所有的点至少需要多少挡板。

这题是二分图匹配。一开始想到了是一张图求最小边覆盖集,但却没想到怎么向二分图转化。其实这题不用转化,因为我们可以给斜行染两种颜色,相邻斜行染不同颜色,然后就会发现其实所有的边(若结点相邻则连边)都是连接的两种颜色的结点,这已经是二分图了。直接在这张图上跑匈牙利算法求解就行了。答案最小边覆盖集就是n-最大匹配。

另外需要注意的是这样连边正反各自连了一遍,匈牙利算法跑的时候也没有区分两种颜色的结点,因此最大匹配要除二。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
int h,w;
const int dx[]={-1,0,1,0};
const int dy[]={0,1,0,-1};
char s[20];
char M[45][20];
int p[45][20],cnt=0;
const int MAXN=410;

int g[MAXN][MAXN];
int linker[MAXN];
bool used[MAXN];
bool dfs(int u) {
for(int v=1;v<=cnt;v++) {
if(g[u][v] && !used[v]) {
used[v]=true;
if(linker[v]==-1 || dfs(linker[v])) {
linker[v]=u;
return true;
}
}
}
return false;
}
int hungary() {
int res=0;
memset(linker,-1,sizeof(linker));
for(int u=1;u<=cnt;u++) {
memset(used,false,sizeof(used));
if(dfs(u)) res++;
}
return res;
}

int main() {
int T;
scanf("%d",&T);
while(T--) {
cnt=0;
memset(p,0,sizeof(p));
memset(M,0,sizeof(M));
memset(g,0,sizeof(g));
scanf("%d%d%*c",&h,&w);
for(int i=1;i<=h;i++) {
scanf("%s",s);
for(int j=1;j<=w;j++) {
M[i][j]=s[j-1];
if(M[i][j]=='*') p[i][j]=++cnt;
}
}
for(int i=1;i<=h;i++)
for(int j=1;j<=w;j++) {
for(int k=0;k<4;k++)
if(i+dx[k]>=1&&i+dx[k]<=h&&j+dy[k]>=1&&j+dy[k]<=w) {
g[p[i][j]][p[i+dx[k]][j+dy[k]]]=1;
}
}
int ans=hungary();
ans=cnt-ans/2;
printf("%d\n",ans);
}
}