二分图入门题

 

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

 

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

 

你可能感兴趣的:(二分图)