1 /*
2 题意:给出一个r*c大小的草地,其中*表示泥地,.表示草地,给出若干宽1,长任意的木板,要求用这些木板垂直或水平
3 覆盖泥地,但是不能覆盖了草地,木板可以相互重叠,问最少要多少块木板
4
5 题解:最小点覆盖 == 最大匹配
6 首先所有的泥地只用横向的木板覆盖,为每个板编号1~n1,然后只用纵向的木板覆盖,为每个板编号1~n2,然后纵横相交
7 的点则加上一条边,从而建立起了二部图,其中的每条边都代表着需要覆盖的点,最后只需求出以最少的点覆盖所有的边
8 即可,即求最小颠覆盖。
9 */
10 #include <cstdio>
11 #include <cstring>
12
13 #define clr(a,b) (memset(a,b,sizeof(a)))
14
15 const int MAXV = 5005;
16 const int MAXE = 10005;
17
18 struct edge
19 {
20 int v,next;
21 }E[MAXE];
22
23 int EN;
24 int mat[MAXV],head[MAXV];
25 bool vis[MAXV];
26 int n;
27
28 void insert(int u, int v)
29 {
30 E[EN].v = v;
31 E[EN].next = head[u];
32 head[u] = EN++;
33 }
34
35 bool find(int t)
36 {
37 int v;
38 for(int i = head[t];i != -1 ;i = E[i].next) {
39 if(vis[ v = E[i].v ])
40 continue;
41 vis[v] = true;
42 if(mat[v] == -1 || find(mat[v])) {
43 mat[v] = t;
44 return true;
45 }
46 }
47 return false;
48 }
49 inline int MaxMatch() {
50 int i,num = 0;
51 clr(mat,-1);
52 for(i = 0;i < n;i ++) {
53 clr(vis,false);
54 num += find(i);
55 }
56 return num;
57 }
58
59 int G[55][55],G1[55][55],G2[55][55];
60
61 int main(void)
62 {
63 int r,c;
64 while (~scanf("%d%d",&r,&c))
65 {
66 char s[55];
67 for(int i=0; i<r; i++)
68 {
69 scanf("%s",s);
70 for(int j=0; j<c; j++)
71 {
72 if ('*' == s[j])
73 G[i][j] = 1;
74 else
75 G[i][j] = 0;
76 }
77 }
78
79 EN = 0;
80 memset(head,-1,sizeof(head));
81
82 n = 0;
83 int n1,n2;
84 n1 = 0;
85 // 横向木板点集
86 for(int i=0; i<r; i++)
87 {
88 for(int j=0; j<c; j++)
89 {
90 if (G[i][j])
91 {
92 G1[i][j] = n1;
93 if (c-1 == j || !G[i][j+1])
94 n1++;
95 }
96 }
97 }
98
99 n2 = 0;
100 // 纵向木板点集
101 for(int i=0; i<c; i++)
102 {
103 for(int j=0; j<r; j++)
104 {
105 if (G[j][i])
106 {
107 G2[j][i] = n2;
108 if (r-1 == j || !G[j+1][i])
109 n2++;
110 }
111 }
112 }
113
114 // 加边
115 for(int i=0; i<r; i++)
116 for(int j=0; j<c; j++)
117 if (G[i][j])
118 insert(G1[i][j],G2[i][j]);
119
120 n = n1;
121
122 printf("%d\n",MaxMatch());
123 }
124 return 0;
125 }