zoukankan      html  css  js  c++  java
  • 二分图入门题

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

     

    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
    ",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
    ",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-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
    ", 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-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
    ",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
    ",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-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
    ", 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
  • 相关阅读:
    VS2005在使用membership的时候,如何连接Access数据库?
    今天想开始写计划的项目,可是就是静不下心来,乱糟糟的!
    今天想开始写计划的项目,可是就是静不下心来,乱糟糟的!
    有钱真好
    网页左边和上面的空隙如何设置成为0
    vim 配色方案(目测有上百个)
    Git 远程仓库的管理和使用
    vim 使用图
    Python 编程挑战
    python 网络爬虫
  • 原文地址:https://www.cnblogs.com/khan724/p/4078596.html
Copyright © 2011-2022 走看看