1 /*
2 题意:给出一个由0和1组成的n*m的矩阵,每一次可以删除整行或者整列的1,问最少进行多少次删除可以将所有的1删除
3
4 题解:最小点覆盖==最大匹配
5 关键是建图:以行号为一个点集,列号为另一点集,矩阵中为1的方格加边(i,j),建立二部图(实际上不一定要以二部图
6 的思路来想,可以首先只是普通的一个建图,由行点集和列点集混合组成,然后在(i,j)加边,最后求这个图的最小点覆
7 盖,只是可以将两个点集分开排列就形成了二部图)
8 */
9 #include <cstdio>
10 #include <cstring>
11
12 #define clr(a,b) (memset(a,b,sizeof(a)))
13
14 const int N = 105;
15
16 struct edge
17 {
18 int v,next;
19 }E[N*N];
20
21 int EN;
22
23 int mat[N],head[N];
24 bool vis[N];
25 int n;
26
27 void insert(int u, int v)
28 {
29 E[EN].v = v;
30 E[EN].next = head[u];
31 head[u] = EN++;
32 }
33
34 bool find(int t)
35 {
36 int v;
37 for(int i = head[t];i != -1 ;i = E[i].next) {
38 if(vis[ v = E[i].v ])
39 continue;
40 vis[v] = true;
41 if(mat[v] == -1 || find(mat[v])) {
42 mat[v] = t;
43 return true;
44 }
45 }
46 return false;
47 }
48 inline int MaxMatch() {
49 int i,num = 0;
50 clr(mat,-1);
51 for(i = 0;i < n;i ++) {
52 clr(vis,false);
53 num += find(i);
54 }
55 return num;
56 }
57
58 int main(void)
59 {
60 int m;
61 while (~scanf("%d",&n) && n)
62 {
63 scanf("%d",&m);
64 EN = 0;
65 memset(head,-1,sizeof(head));
66 for(int i=0; i<n; i++)
67 for(int j=0; j<m; j++)
68 {
69 int t;
70 scanf("%d",&t);
71 if (t)
72 {
73 insert(i,j);
74 }
75 }
76 printf("%d\n",MaxMatch());
77 }
78 return 0;
79 }