二分图入门题

 

  二分图构图的特点是:先根据题意确定考察点,然后再判断构造的图模型是否是二分图,或者能否转换为二分图,然后根据划分关系是否明确来定性边有无方向。最后,用二分图匹配算法解决之。

 

1: HDU 过山车

http://acm.hdu.edu.cn/showproblem.php?pid=2063

分析:直观的二分图题,以男女为二分图的两部分建图即可;

 1 #include <cstdio>
 2 #include <cstring>
 3 using namespace std;
 4 #define N 505
 5 int k, n, m;
 6 bool partner[N][N], used[N];
 7 int match[N];
 8 
 9 bool find(int x)
10 {
11     for (int i=1; i<=n; i++)
12     {
13         if (!used[i] && partner[x][i])
14         {
15             used[i] = true;
16             if (match[i]==-1 || find(match[i]))
17             {
18                 match[i] = x;
19                 return true;
20             }
21         }
22     }
23     return false;
24 }
25 
26 void Hungary ()
27 {
28     int cnt=0;
29     memset (match, -1, sizeof match);
30     for (int i=1; i<=m; i++)
31     {
32         memset (used, 0, sizeof used);
33         if (find(i))
34             cnt++;
35     }
36     printf ("%d\n",cnt);
37 }
38 int main ()
39 {
40     while (~scanf ("%d",&k) && k)
41     {
42         int a, b;
43         scanf ("%d%d",&m, &n);
44         memset (partner, 0, sizeof partner);
45         while (k--)
46         {
47             scanf("%d%d",&a, &b);
48             partner[a][b] = 1;
49         }
50         Hungary();
51     }
52     return 0;
53 }
View Code

 

2: HDU Matrix

http://acm.hdu.edu.cn/showproblem.php?pid=2119

题意:在一个N*M的矩阵中仅有0,1组成,假设每次都可以消去一行或者一列的全部 1,问你最少要几次把全部的 1 消去?

分析:如果我们以 X 和 Y 坐标来分别表示二分图的 X 部分 Y 部分,把1的X,Y位置连一条线。那么一个点覆盖的意义就是,一次能够消灭的所有1的位置,那么最少点覆盖就是我们要求就的答案了。根据二分图的性质:最小点覆盖 = 最大匹配。所以就转化为了求最大匹配问题。

 1 #include <cstdio>
 2 #include <cstring>
 3 using namespace std;
 4 #define N 105
 5 int n, m;
 6 bool used[N];
 7 int match[N], map[N][N];
 8 
 9 bool find(int x)
10 {
11     for (int i=1; i<=m; i++)
12     {
13         if (!used[i] && map[x][i])
14         {
15             used[i] = true;
16             if (match[i]==-1 || find(match[i]))
17             {
18                 match[i] = x;
19                 return true;
20             }
21         }
22     }
23     return false;
24 }
25 
26 void Hungary ()
27 {
28     int cnt=0;
29     memset (match, -1, sizeof match);
30     for (int i=1; i<=n; i++)
31     {
32         memset (used, 0, sizeof used);
33         if (find(i))
34             cnt++;
35     }
36     printf ("%d\n",cnt);
37 }
38 int main ()
39 {
40     while (~scanf ("%d",&n) && n)
41     {
42         scanf ("%d",&m);
43         memset (map, 0, sizeof map);
44         for (int i=1; i<=n; i++)
45             for (int j=1; j<=m; j++)
46                 scanf ("%d",&map[i][j]);
47         Hungary ();
48     }
49     return 0;
50 }
View Code

 

 

   路径覆盖的定义就是: 在有向图中找一些路径,使之覆盖了图中的所有顶点,就是任意一个顶点都跟那些路径中的某一条关联。

  最小路经覆盖  = N - 最大匹配;

3: HDU  Air Raid

http://acm.hdu.edu.cn/showproblem.php?pid=1151

题意:在一个仅有单行道的小镇中,每条道路都连接连个不同的交叉口,并且不存在环。现在,需要往这个小镇的交叉路口派一些伞兵,每个在一个路口着陆了的伞兵可以沿着街去到其他路口;问你,最少需要派多少伞兵可以访问这个小镇的所有街道?

分析:题目就是要我们求最小路径覆盖;

 1 #include <cstdio>
 2 #include <cstring>
 3 #define N 150
 4 
 5 bool used[N], map[N][N];
 6 int match[N], n;
 7 bool dfs (int x)
 8 {
 9     for (int i=1; i<=n; i++)
10     {
11         if (!used[i] && map[x][i])
12         {
13             used[i] = true;
14             if (match[i]==-1 || dfs(match[i]))
15             {
16                 match[i] = x;
17                 return true;
18             }
19         }
20     }
21     return false;
22 }
23 
24 void hungary ()
25 {
26     int cnt=0;
27     memset (match, -1, sizeof match);
28     for (int i=1; i<=n; i++)
29     {
30         memset (used, 0, sizeof used);
31         if (dfs(i)) cnt++;
32     }
33     printf ("%d\n",n-cnt);
34 }
35 
36 int main ()
37 {
38     int t, m, a, b;
39     scanf ("%d",&t);
40     while (t--)
41     {
42         scanf ("%d%d",&n, &m);
43         memset (map, 0, sizeof map);
44         for (int i=0; i<m; i++)
45         {
46             scanf ("%d%d",&a, &b);
47             map[a][b] = 1;
48         }
49         hungary ();
50     }
51     return 0;
52 }
View Code

 

 

4:HDU  Courses

http://acm.hdu.edu.cn/showproblem.php?pid=1083

题意:在一个班上有 N 个学生和 P 门课程,每个人都可以选修 0 门以上的课程,问你是否存在这样一个 P 元素的集合,他满足下面个的关系:

     。每个学生都代表一门课程(相当于课代表)

   。每门课程都可以在这个集合中找到(每门课都必须有一个课代表)

分析:首先,由于这个集合要满足 p 元素,所以,当 N < P 时,显然,人数不够,必然还剩 P-N 门课找不到人来当课代表,故输出 “NO”;而当 P < N 时,我们按照,题目中提供的以课程为 X 部分,学生为 Y 部分建立二分图,若他们之间存在选修于被选修关系, 则连线,那么一个匹配就是一种要求关系。所以,程序只要求匹配数 M 大于等于 P 即可。不过,这题貌似有漏洞,因为我就算把条件改为 M==P 也能AC。

 1 #include <cstdio>
 2 #include <cstring>
 3 #define N 310
 4 
 5 bool used[N], map[N][N];
 6 int match[N], n, p;
 7 bool dfs (int x)
 8 {
 9     for (int i=1; i<=n; i++)
10     {
11         if (!used[i] && map[x][i])
12         {
13             used[i] = true;
14             if (match[i]==-1 || dfs(match[i]))
15             {
16                 match[i] = x;
17                 return true;
18             }
19         }
20     }
21     return false;
22 }
23 
24 void hungary ()
25 {
26     int cnt=0;
27     memset (match, -1, sizeof match);
28     for (int i=1; i<=p; i++)
29     {
30         memset (used, 0, sizeof used);
31         if (dfs(i)) cnt++;
32     }
33     puts (cnt>=p ? "YES":"NO");
34 }
35 
36 int main ()
37 {
38     int t, m, a;
39     scanf ("%d",&t);
40     while (t--)
41     {
42         scanf ("%d%d",&p, &n);
43         memset (map, 0, sizeof map);
44         for (int i=1; i<=p; i++)
45         {
46             scanf ("%d",&m);
47             while (m--)
48             {
49                 scanf ("%d",&a);
50                 map[i][a] = 1;
51             }
52         }
53         if (n < p)
54             puts ("NO");
55         else
56             hungary ();
57     }
58     return 0;
59 }
View Code

 

5:POJ Selecting Courses

http://poj.org/problem?id=2239

题意:LM是一个爱学习的人,每次开学的时候,LM总是希望能够在不课程不冲突的情况下选尽量多的课,一周 7 天,每天12节课,共有上百节课可选。老师每次只能上一个班的课,每门课在一个星期内可能会上几次,比如:老师可以在周二给 7 班上数学课,也可以再周三给 12 班上数学课。你可以认为每个班都是一样的,学生可以自用选择任何一个班级来上他喜欢的课。现在,作为他的朋友,请你告诉他他最多可以选多少课??

分析:初一看,这里总共有(课程,时间,班级)共三种关系,而我们现在是要用二分图的最大匹配来解决,显然,我们必须把其中的连个关系合并到一起,由三元组变成二元组,从而建立二分图来解决。经过分析,我们知道,我们最终需要的确定的是,最多有多少课程在上课时间上没有冲突?从而使得学生可以选择没有冲突的课去上,而班级在这里没有什么影响,因为学生是可以任意选择班级的。所以,我们可以把班级和时间合并到一起看成一个关系。那么,我们该怎么合并呢?如果我们直接相加,那么就会出现一个问题:(p=1,q=12)和(p=2,q=11)本来是不同的元素,但若我们直接相加的话,两个值都等于13就表示相同的元素了,所以,我们可以 (p*12+q)来合并来避免上述问题。

 1 #include <cstdio>
 2 #include <cstring>
 3 using namespace std;
 4 #define N 400
 5 bool used[N], map[N][N];
 6 int match[N], n;
 7 
 8 bool dfs (int x)
 9 {
10     for (int i=1; i<=97; i++)
11     {
12         if (!used[i] && map[x][i])
13         {
14             used[i] = true;
15             if (match[i]==-1 || dfs(match[i]))
16             {
17                 match[i] = x;
18                 return true;
19             }
20         }
21     }
22     return false;
23 }
24 
25 void hungary ()
26 {
27     memset (match, -1, sizeof match);
28     int cnt=0;
29     for (int i=1; i<=n; i++)
30     {
31         memset (used, 0, sizeof used);
32         if (dfs (i)) cnt++;
33     }
34     printf ("%d\n", cnt);
35 }
36 
37 int main()
38 {
39     int p, q, m;
40     while (~scanf ("%d",&n))
41     {
42         memset (map, 0, sizeof map);
43         for (int i=1; i<=n; i++)
44         {
45             scanf ("%d",&m);
46             while (m--)
47             {
48                 scanf ("%d%d",&p, &q);
49                 map[i][p*12+q] = 1;
50             }
51         }
52         hungary ();
53     }
54     return 0;
55 }
View Code

 

6: HDU Girls and Boys

http://acm.hdu.edu.cn/showproblem.php?pid=1068

题意:在大二的时候,有些人就开始恋爱了,所谓的恋爱就是指异性之间的。给你一组数据之间的关系,问你这里面最多有多少人没有谈恋爱??

分析:我们很容易按照给出的数据建图,最大独立集就是我们要求的,在二分图中,我们有:最大独立集 = V - 最大匹配 M;这题需要注意的是,虽然,二分图的模型很明确,但是划分关系部明确。因此,我们建图的时候建成无向边,得到的最大匹配为M,最大独立集为 S ,则有: S = V — M/2;

 1 #include <cstdio>
 2 #include <vector>
 3 #include <cstring>
 4 using namespace std;
 5 #define N 520
 6 
 7 int n, match[N];
 8 bool used[N],map[N][N];
 9 
10 bool dfs(int x)
11 {
12     for (int i=0; i<n; i++)
13     {
14         if (!used[i] && map[x][i])
15         {
16             used[i] = true;
17             if (match[i]==-1 || dfs(match[i]))
18             {
19                 match[i] = x;
20                 return true;
21             }
22         }
23     }
24     return false;
25 }
26 
27 void hungary ()
28 {
29     int cnt=0;
30     memset (match, -1, sizeof match);
31     for (int i=0; i<n; i++)
32     {
33         memset (used, 0, sizeof used);
34         if (dfs(i)) cnt++;
35     }
36     printf ("%d\n",n-cnt/2);
37 }
38 
39 int main()
40 {
41     int x, y, m;
42     while (~scanf ("%d",&n))
43     {
44         memset (map, 0, sizeof map);
45         for (int i=0; i<n; i++)
46         {
47             scanf ("%d: (%d)",&x, &m);
48             while (m--)
49             {
50                 scanf ("%d",&y);
51                 map[x][y] = map[y][x] = true;
52             }
53         }
54         hungary ();
55     }
56     return 0;
57 }
View Code

 

7: POJ  The Perfect Stall

http://poj.org/problem?id=1274

题意:农民FJ上周兴建了一个产奶仓,由于种种原因,某些奶牛只愿在某些摊位产奶,而另一些奶牛只愿在另一些摊位产奶。而一个摊位最多只能容一个奶牛产奶,同样一个奶牛只能占一个摊位。问你最大能使多少奶牛产奶?

分析:直观的二分图模型求最大匹配问题,划分关系也很明确。

 1 #include <cstdio>
 2 #include <cstring>
 3 #define N 220
 4 
 5 bool used[N], map[N][N];
 6 int match[N], n, m;
 7 
 8 bool dfs (int x)
 9 {
10     for (int i=1; i<=m; i++)
11     {
12         if (!used[i] && map[x][i])
13         {
14             used[i] = true;
15             if (match[i]==-1 || dfs(match[i]))
16             {
17                 match[i] = x;
18                 return true;
19             }
20         }
21     }
22     return false;
23 }
24 
25 void hungary ()
26 {
27     int cnt=0;
28     memset (match, -1, sizeof match);
29     for (int i=1; i<=n; i++)
30     {
31         memset (used, 0, sizeof used);
32         if (dfs(i)) cnt++;
33     }
34     printf ("%d\n",cnt);
35 }
36 
37 int main()
38 {
39     int a, b;
40     while (~scanf ("%d%d",&n, &m))
41     {
42         memset (map, 0, sizeof map);
43         for (int i=1; i<=n; i++)
44         {
45             scanf ("%d",&a);
46             while (a--)
47             {
48                 scanf ("%d",&b);
49                 map[i][b] = 1;
50             }
51         }
52         hungary ();
53     }
54     return 0;
55 }
View Code

 

8: POJ  Asteroids

http://poj.org/problem?id=3041

题意:Bessie 想要驾驶他的宇宙飞船通过一片危险的行星场,为了安全,飞船必须避开行星。幸运的是,有一种超能武器可以使行星在一秒内蒸发,由于这个武器很贵,所以,问你最少的用多少这种武器才能消灭掉行星?

分析:这个题,如果他是以矩阵的形式给出行星的位置,那么我们可能还不知道怎么建图,不过,数据给出的方式却给了我们一个建图方法,以行星坐标(X,Y)来分别作为二分图的两部分来建。这样的话,最小点覆盖就刚好是我们所要求的。

 1 #include <cstdio>
 2 #include <cstring>
 3 #define N 550
 4 
 5 bool used[N], map[N][N];
 6 int match[N], n, m;
 7 
 8 bool dfs (int x)
 9 {
10     for (int i=1; i<=n; i++)
11     {
12         if (!used[i] && map[x][i])
13         {
14             used[i] = true;
15             if (match[i]==-1 || dfs(match[i]))
16             {
17                 match[i] = x;
18                 return true;
19             }
20         }
21     }
22     return false;
23 }
24 
25 void hungary ()
26 {
27     int cnt=0;
28     memset (match, -1, sizeof match);
29     for (int i=1; i<=n; i++)
30     {
31         memset (used, 0, sizeof used);
32         if (dfs(i)) cnt++;
33     }
34     printf ("%d\n",cnt);
35 }
36 
37 int main()
38 {
39     int a,b;
40     while (~scanf ("%d%d",&n, &m))
41     {
42         memset (map, 0, sizeof map);
43         for (int i=0; i<m; i++)
44         {
45             scanf ("%d%d",&a, &b);
46             map[a][b] = 1;
47         }
48         hungary ();
49     }
50     return 0;
51 }
View Code

 

9: POJ  Guardian of Decency

http://poj.org/problem?id=2771

题意:FS是一所高中的老师,他想要带学生出去远足,但是他又担心在路上,同学们会发生恋爱关系,不过,据他观察发现,如果两人之间满足下面条件之一的就基本上不会有恋爱关系:

1.两人身高差超过40cm; 2.两人是同性; 3.两人爱好的音乐风格不同; 4.两人有相同的运动爱好。

现在,FS 想带一群人去远足,但是,那群人里面任意两个人都不能有发生恋爱的可能。问你,他最多能带多少人?

分析:显然,我们可以分别以男女为二分图的两部来建图,不过,我们要是以不发生恋爱关系的人之间建边的话,最大匹配并没有意义。所以,我们考虑在两个可能恋爱的之间建边的话,最大独立集就是我们所要求的了。

 

 1 #include <cstdio>
 2 #include <cstring>
 3 #define N 505
 4 
 5 struct Node
 6 {
 7     int h;
 8     char sex[2], mus[105], spr[105];
 9 }node[N];
10 bool used[N], map[N][N];
11 int match[N], n;
12 int fabs (int x)
13 {
14     return x < 0 ? -x:x;
15 }
16 
17 bool dfs (int x)
18 {
19     for (int i=0; i<n; i++)
20     {
21         if (!used[i] && map[x][i])
22         {
23             used[i] = true;
24             if (match[i]==-1 || dfs(match[i]))
25             {
26                 match[i] = x;
27                 return true;
28             }
29         }
30     }
31     return false;
32 }
33 
34 void hungary ()
35 {
36     int cnt=0;
37     memset (match, -1, sizeof match);
38     for (int i=0; i<n; i++)
39     {
40         memset (used, 0, sizeof used);
41         if (dfs(i)) cnt++;
42     }
43     printf ("%d\n",n-cnt);
44 }
45 
46 int main ()
47 {
48     int t;
49     scanf ("%d",&t);
50     while (t--)
51     {
52         scanf ("%d",&n);
53         for (int i=0; i<n; i++)
54             scanf("%d%s%s%s",&node[i].h, node[i].sex,node[i].mus, node[i].spr);
55         memset (map, 0, sizeof map);
56         for (int i=0; i<n-1; i++)
57             for (int j=i+1; j<n; j++)
58             {
59                 if (fabs(node[i].h-node[j].h)<=40 && node[i].sex[0]!=node[j].sex[0] &&
60                 strcmp(node[i].mus,node[j].mus)==0 && strcmp(node[i].spr,node[j].spr))
61                 {
62                     if(node[i].sex[0]=='M')
63                         map[i][j] = 1;
64                     else
65                         map[j][i] = 1;
66                 }
67             }
68         hungary ();
69     }
70     return 0;
71 }
View Code

 

10:  HDU  Machine Schedule

http://acm.hdu.edu.cn/showproblem.php?pid=1150

题意:有两台机器 A 和 B 各有N和M种工作模式,现在给你K份任务,每份任务都可以在A机器的模式 i 或B机器的模式 j 下独立完成。并且K份任务可以按任意顺序完成。不过每次切换模式的时候,都得花时间手动重启,问你,完成所有任务最少的重启次数是多少?

分析:首先,题目给出了(i,x,y)三元组的关系,若我们刚开始着眼于任务 i不放,一直试图让任务 i 和模式 x,y建立一个二分图,显然,若各自独立与X,y建图,这就不是二分图,而是三分图了。若我们把x,y加起来建图,那么模式切换操作将无法体现出来。仔细看题,我们发现,整个过程只有A B两种机器,刚好符合二分图的构造。若我们在(i,x,y)的关系中选择x-y来建边。那么一个匹配刚好(也一定是)是完成某个任务 Ki 在机器A 和机器B的模式(也就是一个匹配==一个任务)。图中任一个定点都是一个模式,而我们所求的就是用最少的顶点(模式)覆盖所的边(完成所有任务)。

 1 #include <cstdio>
 2 #include <cstring>
 3 using namespace std;
 4 #define N 110
 5 
 6 int n, m, k;
 7 bool used[N], map[N][N];
 8 int match[N];
 9 
10 bool dfs(int x)
11 {
12     for (int i=1; i<=m; i++)
13     {
14         if (!used[i] && map[x][i])
15         {
16             used[i] = true;
17             if (match[i]==-1 || dfs(match[i]))
18             {
19                 match[i] = x;
20                 return true;
21             }
22         }
23     }
24     return false;
25 }
26 
27 void hungary ()
28 {
29     int cnt=0;
30     memset (match, -1, sizeof match);
31     for (int i=1; i<=n; i++)
32     {
33         memset (used, 0, sizeof used);
34         if (dfs(i)) cnt++;
35     }
36     printf ("%d\n", cnt);
37 }
38 int main()
39 {
40     int a, x, y;
41     while (~scanf("%d",&n) && n)
42     {
43         scanf ("%d%d",&m, &k);
44         memset (map, 0, sizeof map);
45         for (int i=0; i<k; i++)
46         {
47             scanf ("%d%d%d",&a,&x,&y);
48             map[x][y] = 1;
49         }
50         hungary ();
51     }
52     return 0;
53 }
View Code

 

posted @ 2014-11-06 13:51  无道圣君  阅读(312)  评论(0编辑  收藏  举报