• 二分图及其衍生


    目录

    1.关押罪犯(二分+染色法)

    2.棋盘覆盖(最大匹配数)

    3.机器任务(最小点覆盖)

    4.骑士放置(最大独立集)

    5.捉迷藏(最小路径重复点覆盖)


    1.关押罪犯(二分+染色法)

    257. 关押罪犯 - AcWing题库

    二分罪犯的冲突事件的影响力,二分的条件是大于mid的罪犯的怒气值放在不同的监狱,然后用染色法,假如没冲突就,缩小mid

    1. #include
    2. using namespace std;
    3. const int N=20010,M=200010;
    4. int n,m;
    5. int h[N],e[M],ne[M],idx,w[M];
    6. int color[N];//0表示还没颜色,1表示染黑色,2表示白色
    7. void add(int a,int b,int c)
    8. {
    9. e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++;
    10. }
    11. bool dfs(int u,int c,int mid)//染到u这个点,目前颜色为c,二分值为mid
    12. {
    13. color[u]=c;//让这个点颜色为c
    14. for(int i=h[u];~i;i=ne[i])
    15. {
    16. int j=e[i];
    17. if(w[i]<=mid) continue;//假如小于就不用处理了
    18. if(color[j])//假如有颜色了
    19. {
    20. if(color[j]==c) return false; //假如颜色相同的话,则不符合
    21. }
    22. else if(!dfs(j,3-c,mid)) return false;//3-c就是给j这个点然另一种颜色
    23. }
    24. return true;
    25. }
    26. bool check(int mid)
    27. {
    28. memset(color,0,sizeof color);//清空
    29. for(int i=1;i<=n;i++)
    30. if(!color[i])//假如没有被染色过
    31. if(!dfs(i,1,mid)) return false;//假如染色后不成立
    32. return true;
    33. }
    34. int main()
    35. {
    36. cin>>n>>m;
    37. memset(h,-1,sizeof h);
    38. while(m--)
    39. {
    40. int a,b,c;
    41. cin>>a>>b>>c;
    42. add(a,b,c),add(b,a,c);
    43. }
    44. int l=0,r=1e9;
    45. while(l//二分查找最小冲突事件的影响力
    46. {
    47. int mid=(l+r)>>1;
    48. if(check(mid)) r=mid;
    49. else l=mid+1;
    50. }
    51. cout<
    52. return 0;
    53. }

    增广路径:从非匹配点走然后非匹配边然后匹配边然后非匹配然后匹配....最后是非匹配点

    最大匹配等价于不存在增广路径

    2.棋盘覆盖(最大匹配数)

    372. 棋盘覆盖 - AcWing题库

    1. #include
    2. #define x first
    3. #define y second
    4. using namespace std;
    5. typedef pair<int,int> pii;
    6. const int N=110;
    7. pii match[N][N];
    8. bool st[N][N],g[N][N];
    9. int n,m,res=0;
    10. int dx[4]={-1,0,1,0},dy[4]={0,1,0,-1};
    11. bool find(int x,int y)//帮x,y这个点找对象
    12. {
    13. for(int i=0;i<4;i++)//枚举他可能的对象
    14. {
    15. int a=x+dx[i],b=y+dy[i];
    16. if(a<1||a>n||b<1||b>n) continue;//假如越界
    17. if(st[a][b]||g[a][b]) continue;//假如这个点有其他的对象
    18. st[a][b]=true;//先标记这个点有对象了
    19. pii t=match[a][b];
    20. if(t.x==0||find(t.x,t.y))//假如这个点还没对象或者这个点有对象,但是可以让给别人
    21. {
    22. match[a][b]={x,y};//则把a,b给x,y
    23. return true;
    24. }
    25. }
    26. return false;//反之没对象
    27. }
    28. int main()
    29. {
    30. cin>>n>>m;
    31. while(m--)
    32. {
    33. int a,b;
    34. cin>>a>>b;
    35. g[a][b]=true;
    36. }
    37. for(int i=1;i<=n;i++)
    38. for(int j=1;j<=n;j++)
    39. if((i+j)&1&&!g[i][j])//枚举奇数的情况
    40. {
    41. memset(st,0,sizeof st); //清空上一层的状态
    42. if(find(i,j)) res++;//假如这个点找的到对象,则答案++
    43. }
    44. cout<
    45. return 0;
    46. }

    最小点覆盖:每条边至少选出一个点,使得选出的点最小就是最小点覆盖

    最小点覆盖 等于 最大匹配数 

    3.机器任务(最小点覆盖)

    376. 机器任务 - AcWing题库

    这题ai与bi可以看成ai->bi,然后答案就是选最少的点使得所有边都覆盖了,其实就是最小点覆盖问题,就是求最大匹配数即可

    1. #include
    2. using namespace std;
    3. const int N=110;
    4. bool g[N][N],st[N];
    5. int match[N];
    6. int n,m,k;
    7. bool find(int u)//帮u找女朋友
    8. {
    9. for(int i=1;i//枚举可能的女朋友
    10. if(!st[i]&&g[u][i])//假如这个女朋友没有男朋友并且我跟他有关系
    11. {
    12. st[i]=true;//标记这个点有男朋友了
    13. int t=match[i];//找一下这个点的男朋友
    14. if(t==0||find(t))//假如没男朋友或者她男朋友可以换个女朋友
    15. {
    16. match[i]=u;//则让她的男朋友是我
    17. return true;//返回找到了
    18. }
    19. }
    20. return false;//反之没女朋友
    21. }
    22. int main()
    23. {
    24. while(cin>>n,n)
    25. {
    26. memset(g,0,sizeof g);//清空
    27. memset(match,0,sizeof match);//清空
    28. cin>>m>>k;
    29. while(k--)
    30. {
    31. int t,a,b;
    32. cin>>t>>a>>b;
    33. g[a][b]=true;//从a->b连条边
    34. }
    35. int res=0;
    36. for(int i=1;i
    37. {
    38. memset(st,0,sizeof st);//清空
    39. if(find(i)) res++;//假如匹配成功
    40. }
    41. cout<
    42. }
    43. return 0;
    44. }

    最大独立集:一个图中,选出最多的点,使得选出的点内部没有边 

    最大团:一个图中,选出最多的点,使得选出的点任意两点都有边

    最大独立集与最大团互补

    最大独立集等价于在原图中破环最少的点,将所有边都破坏掉

    等价于 找最小点覆盖 等价于 找最大匹配

    4.骑士放置(最大独立集)

    378. 骑士放置 - AcWing题库

    把所有可以攻击到的马连条边,则答案就是求两两不能攻击到的马,也即最大独立集的问题

    把奇数和偶数分别为两个集合,也即符合二分图,然后八个方向攻击到的格子都与自己的格子的奇偶性不同,所有可以用二分图最大独立集来求解

    答案就是n*m-k-最大匹配数

    1. #include
    2. #define x first
    3. #define y second
    4. using namespace std;
    5. typedef pair<int,int> pii;
    6. const int N=110;
    7. bool g[N][N],st[N][N];
    8. pii match[N][N];
    9. int n,m,k;
    10. int dx[8]={-2,-1,1,2,2,1,-1,-2},dy[8]={1,2,2,1,-1,-2,-2,-1};//八个方向
    11. bool find(int x,int y)
    12. {
    13. for(int i=0;i<8;i++)//枚举可能的女朋友
    14. {
    15. int a=x+dx[i],b=y+dy[i];
    16. if(a<1||a>n||b<1||b>m) continue;//假如越界
    17. if(st[a][b]||g[a][b]) continue;//假如已经有男朋友了,或者是坏格子
    18. st[a][b]=true;//标记这个点已经有男朋友了
    19. pii t=match[a][b];
    20. if(t.x==0||find(t.x,t.y))//假如没有男朋友或者这个男的可以换个女朋友
    21. {
    22. match[a][b]={x,y};//就把这个女的抢过来给我
    23. return true;//返回有女朋友
    24. }
    25. }
    26. return false;//反之没
    27. }
    28. int main()
    29. {
    30. cin>>n>>m>>k;
    31. for(int i=1;i<=k;i++)//标记所有坏的格子
    32. {
    33. int a,b;
    34. cin>>a>>b;
    35. g[a][b]=true;
    36. }
    37. int res=0;
    38. for(int i=1;i<=n;i++)
    39. for(int j=1;j<=m;j++)
    40. if(!g[i][j]&&(i+j)&1)//假如是奇数和不是坏格子
    41. {
    42. memset(st,0,sizeof st);//清空
    43. if(find(i,j)) res++;//假如找的到女朋友
    44. }
    45. cout<//输出最大独立集
    46. return 0;
    47. }

    最小路径点覆盖: 对于一个有向无环图(DAG),用最少的互不相交的路径将所有点覆盖

    最小路径重复点覆盖:求一遍原图的传递闭包,再求一遍最小路径点覆盖则就是

    5.捉迷藏(最小路径重复点覆盖)

    379. 捉迷藏 - AcWing题库

    题目就是从任意一个点出发,都不能走到另外一个点,答案就是n-最小路径重复点覆盖

    1. #include
    2. using namespace std;
    3. const int N=210;
    4. bool g[N][N],st[N];
    5. int match[N];
    6. int n,m;
    7. bool find(int x)//帮x找女朋友
    8. {
    9. for(int i=1;i<=n;i++)//枚举可能的女朋友
    10. if(!st[i]&&g[x][i])//假如这个女的没男朋友并且我跟他有关系
    11. {
    12. st[i]=true;//标记这个女的找到了男朋友
    13. int t=match[i];
    14. if(t==0||find(t))//假如这个女的没男朋友或着那男的可以换个女朋友
    15. {
    16. match[i]=x;//则把这个女的让给我
    17. return true;//返回找到了
    18. }
    19. }
    20. return false;//反之找不到
    21. }
    22. int main()
    23. {
    24. cin>>n>>m;
    25. while(m--)
    26. {
    27. int a,b;
    28. cin>>a>>b;
    29. g[a][b]=true;
    30. }
    31. //做一遍传递闭包
    32. for(int k=1;k<=n;k++)//floryd求传递闭包
    33. for(int i=1;i<=n;i++)
    34. for(int j=1;j<=n;j++)
    35. g[i][j]|=g[i][k]&g[k][j];
    36. int res=0;
    37. for(int i=1;i<=n;i++)
    38. {
    39. memset(st,0,sizeof st);//清空
    40. if(find(i)) res++;//假如找的到女朋友
    41. }
    42. cout<//输出最大路径重复点覆盖
    43. return 0;
    44. }

  • 相关阅读:
    在Sprinng Boot中使用Redis充当缓存
    以入库时间创建分区表
    【C++】stack/queue/list
    MySQL之DML
    dbeaver导入excel数据
    vue中的filters(源码分析)
    Swin Transformer网络模型
    Matlab(GUI程式设计)
    PDF格式转JPG格式怎么转?掌握方法其实很简单
    构建数据驱动的文化价值体系,还得靠数据分析
  • 原文地址:https://blog.csdn.net/m0_63729880/article/details/126517057