Battle ships

做的时候能不能搜索想了半天。。。后来查了一下竟然是二分图经典模型。。。所以觉得应该记下来。。。

题目大意:
在海上(*)放战舰,任意两个战舰不能出现在同一行或列,除非中间有冰山(#)相隔。问最多放多少战舰。

解题思路:
现将行和列,以冰山(#)为分隔,分割成多段。对于每一个可能放置的位置(*),将其所在的行和列的分段相连。表达的意思就是,这个点如果放置,那么相邻的同一区段(*、o)都不能再用。对按行分割按列分割这两个点集做一次二分图匹配即可。

参考资料

代码:

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
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

typedef long long ll;
const int N=2505;
struct Edge {
int go,next;
} eg[N<<1];
char mp[55][55];
int mx[55][55];
bool vis[N];
int res[N],last[N];
int tot;

void adde(int x,int y)
{
eg[++tot].go=y;
eg[tot].next=last[x];
last[x]=tot;
}

bool dfs(int u)
{
for (int i=last[u];i;i=eg[i].next) {
int &v=eg[i].go;
if (!vis[v]) {
vis[v]=1;
if (!res[v]||dfs(res[v])) {
res[v]=u;
return true;
}
}
}
return false;
}

int main()
{
int T,n,m;
scanf("%d",&T);
while (T--) {
scanf("%d%d",&n,&m);
int nx=0,ny=0;tot=0;
//nx为按行分割的点数,ny为按列分割的点数,tot为邻接表边数.
memset(last,0,sizeof(last));
for (int i=0;i<n;i++) {
scanf("%s",mp[i]);
int f=0;
for (int j=0;j<m;j++) {
if (mp[i][j]=='*') {
if (!f) {
mx[i][j]=++nx;
f=1;
} else {
mx[i][j]=nx;
}
} else if (mp[i][j]=='#'&&f==1) {
f=0;
}
}
}
for (int j=0;j<m;j++) {
int f=0;
for (int i=0;i<n;i++) {
if (mp[i][j]=='*') {
if (!f) {
adde(mx[i][j],++ny);
f=1;
} else {
adde(mx[i][j],ny);
}
} else if (mp[i][j]=='#'&&f==1) {
f=0;
}
}
}
memset(res,0,sizeof(res));
int ans=0;
for (int i=1;i<=nx;i++) {
memset(vis,0,sizeof(vis));
if (dfs(i)) ans++;
}
printf("%d\n",ans);
}
return 0;
}