[关闭]
@darkproject 2017-03-30T10:03:44.000000Z 字数 3522 阅读 785

二分图专题

集训队


A - COURSES (POJ - 1469)

题意大致是P门课n个学生,学生可以选一门课程或者不选,但要求每门课程都要被选上并且是不同的学生代表。
我们可以将学生与课程二分建图,判断其最大匹配数是否满足条件,满足即为yes

  1. #include <iostream>
  2. #include <cstdio>
  3. #include <algorithm>
  4. #include <cstring>
  5. #include <vector>
  6. const int maxn = 405;
  7. using namespace std;
  8. int un, vn, g[maxn][maxn];
  9. int linker[maxn], check[maxn];
  10. bool dfs(int u) {
  11. int v;
  12. for (v = 1; v<=vn; v++)
  13. {
  14. if (g[u][v] && !check[v])
  15. {
  16. check[v] = 1;
  17. if (linker[v] == -1 || dfs(linker[v]))
  18. {
  19. linker[v] = u;
  20. return true;
  21. }
  22. }
  23. }
  24. return false;
  25. }
  26. int hungarian()
  27. {
  28. int ans = 0;
  29. memset(linker, -1, sizeof(linker));
  30. for (int u = 1; u<=un; u++)
  31. {
  32. memset(check, 0, sizeof(check));
  33. if (dfs(u)) ans++;
  34. }
  35. return ans;
  36. }
  37. int main()
  38. {
  39. int p, n, a, b, t;
  40. scanf("%d", &t);
  41. while (t--)
  42. {
  43. memset(g,0,sizeof(g));
  44. scanf("%d%d", &p, &n);
  45. un = p, vn = n;
  46. for (int i = 1; i<=p; ++i)
  47. {
  48. scanf("%d", &a);
  49. for (int j = 0; j<a; j++)
  50. {
  51. scanf("%d", &b);
  52. g[i][b] = 1;
  53. }
  54. }
  55. if (hungarian()==p) printf("YES\n");
  56. else printf("NO\n");
  57. }
  58. return 0;
  59. }

B - The Accomodation of Students HDU - 2444

N个学生他们可能认识也可能不认识,现考虑能否将他们分为2组,使2组的学生都是熟人,每组内的学生都是陌生人,如果可以分成2组算出需要多少房间,不可以则输出No
ps:判断是否为二分图:选取某个点作为起点并染为某种颜色、同时把它相邻的元素染为对立的颜色,进行搜索,如果到那步发现当前点和相邻点的颜色一样,那么就出现了矛盾,就不是二分图。

  1. #include <iostream>
  2. #include <cstdio>
  3. #include <vector>
  4. #include <algorithm>
  5. #include <cstring>
  6. const int maxn=205;
  7. using namespace std;
  8. typedef vector<int>::iterator mxr;
  9. int n,m,color[maxn],check[maxn],linker[maxn];
  10. //vector<int>un;
  11. vector<int>G[maxn];
  12. bool judge(int v,int c)
  13. {
  14. color[v]=c;
  15. for(int i=0;i<G[v].size();i++)
  16. {
  17. if(color[G[v][i]]==c) return false;
  18. if(color[G[v][i]]==0&&!judge(G[v][i],-c)) return false;
  19. }
  20. return true;
  21. }
  22. bool solve()
  23. {
  24. memset(color,0,sizeof(color));
  25. for(int i=0;i<n;++i)
  26. if(color[i]==0)
  27. if(!judge(i,1))
  28. return false;
  29. return true;
  30. }
  31. bool dfs(int u)
  32. {
  33. int v;
  34. for(mxr i=G[u].begin();i!=G[u].end();i++)
  35. {
  36. v=*i;
  37. if(!check[v])
  38. {
  39. check[v]=true;
  40. if(linker[v]==-1||dfs(linker[v]))
  41. {
  42. linker[v]=u;
  43. return true;
  44. }
  45. }
  46. }
  47. return false;
  48. }
  49. int hungarian()
  50. {
  51. int ans=0;
  52. memset(linker,-1,sizeof(linker));
  53. //mxr it;
  54. //for(it=un.begin();it!=un.end();++it)
  55. for(int i=1;i<=n;i++)
  56. {
  57. memset(check,0,sizeof(check));
  58. if(dfs(i)) ans++;
  59. }
  60. return ans;
  61. }
  62. void init()
  63. {
  64. //un.clear();
  65. for(int i=0;i<maxn;i++)
  66. G[i].clear();
  67. }
  68. int main()
  69. {
  70. int x,y;
  71. while(scanf("%d%d",&n,&m)!=EOF)
  72. {
  73. init();
  74. for(int i=0;i<m;++i)
  75. {
  76. scanf("%d%d",&x,&y);
  77. // un.push_back(x);
  78. G[x].push_back(y);
  79. }
  80. if(!solve())
  81. printf("No\n");
  82. else
  83. {
  84. printf("%d\n",hungarian());
  85. }
  86. }
  87. return 0;
  88. }

C - Guardian of Decency POJ - 2771

老师带学生出去旅游,但是担心学生会发生恋爱关系,所以带出去的学生至少要满足以下要求之中的一个:
1.身高相差40cm以上
2.同性
3.喜欢的音乐风格不同
4.喜欢的运动相同
问最多可以带出去多少学生?
最大独立集:最大的一个集合,其中的每两点之间都不存在边
最大独立集=顶点数-最大匹配数
ps:因为dfs扫点扫了2遍,所以最大匹配数/2

  1. #include <iostream>
  2. #include <cstdio>
  3. #include <algorithm>
  4. #include <string.h>
  5. #include <math.h>
  6. #include <cstring>
  7. const int maxn = 505;
  8. using namespace std;
  9. int check[maxn], linker[maxn];
  10. int map[maxn][maxn];
  11. int t, n;
  12. struct mxr
  13. {
  14. int hei;
  15. char sex;
  16. char music[105];
  17. char sport[105];
  18. }stu[maxn];
  19. bool judge(mxr a, mxr b)
  20. {
  21. if (a.sex != b.sex&&strcmp(a.music, b.music) == 0 && abs(a.hei - b.hei) <= 40 && strcmp(a.sport, b.sport) != 0)
  22. return true;
  23. return false;
  24. }
  25. bool dfs(int u)
  26. {
  27. for (int v = 0; v<n; v++)
  28. {
  29. if (map[u][v] && !check[v])
  30. {
  31. check[v] = 1;
  32. if (linker[v] == -1 || dfs(linker[v]))
  33. {
  34. linker[v] = u;
  35. return true;
  36. }
  37. }
  38. }
  39. return false;
  40. }
  41. int hungarian()
  42. {
  43. int ans = 0;
  44. memset(linker, -1, sizeof(linker));
  45. for (int i = 0; i<n; i++)
  46. {
  47. memset(check, 0, sizeof(check));
  48. if (dfs(i)) ans++;
  49. }
  50. return ans;
  51. }
  52. int main()
  53. {
  54. scanf("%d", &t);
  55. while (t--)
  56. {
  57. memset(map, 0, sizeof(map));
  58. scanf("%d", &n);
  59. for (int i = 0; i<n; ++i)
  60. scanf("%d %c %s %s", &stu[i].hei, &stu[i].sex, stu[i].music,stu[i].sport);
  61. for (int i = 0; i<n; ++i)
  62. for (int j = 0; j<n; j++)
  63. {
  64. if (judge(stu[i], stu[j]))
  65. map[i][j] = 1;
  66. }
  67. printf("%d\n", n - hungarian()/2);
  68. }
  69. return 0;
  70. }

自补:
codeforce #round 400 (div1+div2) D. The Door Problem

m个开关控制n个门,每个门由2个开关控制。门的状态由0,1表示,问任意按下开关是否可以使所有的门为0状态。此题可以用并查集或者二分图做,将开关视为点,门的状态作为点边连接。开关分为2个状态按与不按,可以将其分为i+m与i来表示这个状态,最后判断是否在一个并查集里同时存在i+m与m即可,若同时存在即为NO。二分图将开关按下与不按下分为2个集合然后dfs判断图中是否存在环,若存在即为NO。

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注