文章目录

  • 最大团问题和最大独立集
  • 二分图、用网络流解决最大二分匹配的方法
  • 一种另类的增广路——交替路、匈牙利算法
  • 一般图、二分图中的其它性质
  • P1640 [SCOI2010]连续攻击游戏

最大团问题和最大独立集

最大团的定义:
设一个无向图G(V,E)G(V,E)G(V,E),VVV为点集,EEE为两点间的边集。设UUU为VVV的一个子集,若对于任意的结点对uuu,vvv属于UUU都有边连通,则称点集U构成的图为 完全子图 。无向图GGG的完全子图UUU是GGG的团,GGG的最大团即为GGG的最大完全子图。


众所周知,最大团问题是一个NP完全问题,有很多求解它的 名字中二而且牛逼的 算法,此处一概不叙述。

独立集:
图G(V,E)G(V,E)G(V,E)中互不相邻的点构成的集合为独立集。

最大团与最大独立集的关系:
求解一个图中的最大独立街等价于求解其补图的最大团。
独立集的条件是任意两个点互不连通,那么如果把原图中连通的点之间的边删除,不连通点连接,即转化为求个数最多的两两连通点集,也即求最大团。

用DFS+剪枝,实现的求最大团的算法。
P1692 最大团模板

#include<bits/stdc++.h>
using namespace std; int n,m;
int c[305][305];
int ans[305],bestn=0;
int s[305];void dfs(int x,int cnt)
{if(cnt>bestn){bestn=cnt;for(int i=1;i<=n;++i)ans[i]=s[i];}//updateif(x>n) return;if(n-x+1+cnt<=bestn) return;//purningdfs(x+1,cnt);//not pickfor(int i=1;i<x;++i)if(s[i]&&c[i][x])return;s[x]=1;dfs(x+1,cnt+1);s[x]=0;//pickreturn;
}int main()
{cin>>n>>m;for(int i=0,a,b;i<m;++i){cin>>a>>b;c[a][b]=c[b][a]=1;}dfs(1,0);cout<<bestn<<endl; for(int i=1;i<=n;++i)cout<<ans[i]<<" ";return 0;
}

二分图、用网络流解决最大二分匹配的方法

二分图
设图G(V,E)G(V,E)G(V,E)是一个无向图,如果图GGG中的点可以划分为两个没有交集的点集V1V1V1和V2V2V2,并且每条边的两个端点分别在这个两个点集中,则称这样的图GGG为一个二分图。

二分匹配:
给定一个二分图G(V,E)G(V,E)G(V,E),对于它的一个子图MMM,如果MMM中任意的两条边都不连接同一个端点,则称MMM为一个匹配。
最大二分匹配,要求MMM中包括的点最多。

二分匹配与网络流的关系:
在二分匹配中,我们规定所有点集V1V1V1连向的边都指向点集V2V2V2,这样就能在不改变二分匹配性质的基础上转化为一个有向图。由于匹配的两个结点完全占用一条边,所以不妨设所有边的 “ 容量 ” 都为1。二分图显然是一个多源点、多汇点的图,但是并没有要求必须由哪个源点和哪个汇点一定要匹配。因此,可以考虑缩点,把所有源点合并,所有汇点合并。而这在网络流问题中有一个很好用的技巧,就是建立超级源点超级汇点。建立一组边关系,超级源点sss指向点集V1V1V1中所有的点;同理,使点集V2V2V2中所有的点都有指向超级汇点ttt的边。为了符合二分匹配的性质,要求这些新增的边的权值都为1。处理最大二分匹配的问题即转化为计算最大流的问题(或者说寻找所有的增广路径),只不过,每一次增加的流大小只能是1,当没有增广路径的同时,答案也就显然了。

不清楚网络流的同学可以看这一篇博客图论——最大流的增广路相关算法
参考模板题:P3386 【模板】二分图匹配
这一百来行dinic算法的运行效率(两个点集的点数各小于等于1000):

Code

#include<iostream>
#include<utility>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<vector>
using namespace std;struct EDGE{int v;int c;int rev;
};
vector<EDGE> edge[2003];
queue<int> q;
int iter[2003];
bool vis[2003];
int level[2003];
int n,m,e,s,t;void dinic_bfs()
{memset(level,-1,sizeof(level));level[s]=0;q.push(s);while(!q.empty()){int u=q.front();q.pop();for(int i=0;i<edge[u].size();++i){int v=edge[u][i].v;if(edge[u][i].c>0&&level[v]<0){ level[v]=level[u]+1;q.push(v); } }}
}
int dinic_dfs(int u,int f)
{if(t==u) return f;for(int &i=iter[u];i<edge[u].size();++i){EDGE &eg=edge[u][i];if(eg.c>0&&level[u]<level[eg.v]){int d=dinic_dfs(eg.v,min(f,eg.c));if(d>0){eg.c-=d;edge[eg.v][eg.rev].c+=d;return d;} }}return 0;
}
int main()
{cin>>n>>m>>e;s=n+m+1;t=n+m+2;for(int i=1;i<=e;++i){int u,v;cin>>u>>v;if(u>n||v>m) continue;v+=n;EDGE in;in.v=v,in.c=1,in.rev=edge[v].size();edge[u].push_back(in);if(!vis[u])//建立超级源点{in.v=u,in.c=1,in.rev=edge[u].size();edge[s].push_back(in);in.v=s,in.c=0,in.rev=edge[s].size()-1;edge[u].push_back(in);vis[u]=true;}in.v=u,in.c=0,in.rev=edge[u].size()-1;edge[v].push_back(in);if(!vis[v])//建立超级汇点 {in.v=t,in.c=1,in.rev=edge[t].size();edge[v].push_back(in);in.v=v,in.c=0,in.rev=edge[v].size()-1;edge[t].push_back(in);vis[v]=true;}}int sumflow=0;//dinic while(1){dinic_bfs();if(level[t]<0) break;memset(iter,0,sizeof(iter));int addflow;while(1){addflow=dinic_dfs(s,0xffffff);if(!addflow) break;sumflow+=addflow;}}cout<<sumflow<<endl;return 0;
}

一种另类的增广路——交替路、匈牙利算法

交替路:
通过上面对dinic算法的分析,很明显可以看出,使用网络流算法解决二分匹配问题是没有必要的。就像处理边权为1的单源最短路径,没有必要使用dijkstra算法。网络流中寻找增广路以及建立反向边的操作实际是在浪费时间,完全可以用 交替路 处理。交替路适用于无向图,从一个为匹配点出发,一次走未匹配边、匹配边、未匹配边…直到遇到未匹配点,这样就构建出一条增广路径,沿着这条增广路径,对边的性质取反。



匈牙利算法:
寻找交替路是匈牙利算法的核心,每找到一次增广路,对边进行反转,当某个点集遍历完一遍没有新的增广路时,算法终止,此时满足最大二分匹配。以下证明一下匈牙利算法的正确性:

定理一:满足最大匹配的充要条件是没有新的增广路
按照增广路的选取规则,每次选择的路径都是按未匹配、匹配、未匹配的规则进行,每一次增广都会增加一个匹配。假设图G(V,E)G(V,E)G(V,E)此时满足最大匹配,显然匹配数已经无法增加,所以增广路必然不存在。如果一个图不满足最大匹配,就一定能找到一个新的匹配,也即一定会有一条新的增广路径。(和网络流中增广路性质等同)

定理二:如果某一个点没有新的增广路,那么后面的点的增广路径与该点无关。
假设某一个点uuu没有新的增广路径,而后面某一个点u′u'u′的增广路径经过了uuu,那就说明在这之前的某一个结点一定也存在一条相对短的增广路径经过uuu。同时可以推出,如果没有新的结点加入,而且最后一个点也没有新的增广路径,那么整个图就没有增广路径,所以,匈牙利算法中只要对其中一个点集遍历一遍,就能找到最大二分匹配。

匈牙利算法的实现,可以基于DFS或BFS,下面是一个DFS的版本。
P3386 【模板】二分图匹配
Code

#include<iostream>
#include<vector>
#include<cstdio>
#include<cstring>
#include<utility>
using namespace std;
int match[2003];
bool vis[2003];
vector<int> edge[2003];
int n,m,e;
int dfs(int u)
{for(int i=0;i<edge[u].size();++i){int v=edge[u][i];if(!vis[v]){vis[v]=true;if(match[v]==0||dfs(match[v])){match[v]=u;match[u]=v;return 1;}}}return 0;
}
int main()
{scanf("%d %d %d",&n,&m,&e);for(int i=1;i<=e;++i){int u,v;scanf("%d %d",&u,&v);if(u>n||v>m) continue;v+=n;edge[v].push_back(u);edge[u].push_back(v);}int ans=0;for(int i=1;i<=n;++i)if(!match[i]){memset(vis,0,sizeof(vis));ans+=dfs(i);}cout<<ans<<endl;return 0;
}

一般图、二分图中的其它性质

最小边覆盖:
边覆盖指图G(V,E)G(V,E)G(V,E)中的一个边子集,满足图上的每一个结点都与这个边集合关联。
最小边覆盖中,这个边子集包含的边数最少。

最小点覆盖:
点覆盖值图G(V,E)G(V,E)G(V,E)中的一个点子集,满足图中的每一个边都与这个点集合关联。
最小点覆盖中,这个点子集包含的点数最少。

对于一个连通图:最大匹配+最小边覆盖=结点数

最大独立集+最小顶点覆盖=结点数

二分图中:最小顶点覆盖=最大二分匹配

P1640 [SCOI2010]连续攻击游戏


——题解——
做一道最大二分匹配的题目练练手。本题有两个思路,一个是对装备进行匹配,一个是对属性进行匹配。但是,题目要求,攻击必须满足属性值连续且递增,所以不妨对属性进行匹配。当存在某个属性不存在或无法被匹配时,即可得出符合题意的最大匹配度。

——Code——

#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<queue>
using namespace std;
int match[1010006];
bool vis[1010006];
bool maxvis[10004];
vector<int> edge[1010006];
int n;
int dfs(int u)
{for(int i=0;i<edge[u].size();++i){int v=edge[u][i];if(!vis[v]){vis[v]=true;if(!match[v] || dfs(match[v])){match[v]=u;match[u]=v;return 1;}}}return 0;
}
int main()
{scanf("%d",&n);for(int i=1;i<=n;++i){int in1,in2;scanf("%d %d",&in1,&in2);edge[in1].push_back(i+10000);edge[in2].push_back(i+10000);edge[i+10000].push_back(in1);edge[i+10000].push_back(in2);maxvis[in1]=true;maxvis[in2]=true;}int ans=0;for(int i=1;i<=10000;++i){if(!maxvis[i]) break;if(!match[i]){memset(vis,0,sizeof(vis));ans+=dfs(i);}if(!match[i]) break;}printf("%d\n",ans);return 0;
}

图论——最大团问题和最大独立集、二分图相关相关推荐

  1. 图论 最大团,最大独立集

    经典的NP完全问题,只有暴力解,时间复杂度O(n2^n) 对于无向图来说 所谓最大团, 其实就是找一个最大完全子图,最大就是包含的点最多. 而最大独立集 == 补图的最大团 这里使用深度优先搜索实现, ...

  2. 2019牛客暑期多校训练营(第五场)F - maximum clique 1 (最大团:补图最大独立集)

    题目链接 题意 求一个最大的集合,集合内的数字两两之间满足不同bit数大于1 最大团:在一个无向图中找出一个点数最多的完全图 思路 求补图的最大独立集:二分图匹配.最小割 可以将原图转为二分图,指定建 ...

  3. HDU - 3829 Cat VS Dog(最大独立集-二分图最大匹配)

    题目链接:点击查看 题目大意:给出n只狗和m只猫,现在有p个小朋友,每个小朋友都有一只喜欢的猫和一只不喜欢的狗,或者有一只喜欢的狗和一只不喜欢的猫,如果一个小朋友喜欢的动物还在,不喜欢的动物走了,那么 ...

  4. CH - 6901 骑士放置(二分图最大独立集-二分图最大匹配+奇偶拆点)

    题目链接:点击查看 题目大意:给出一个n*m的棋盘,有t个点是禁止放棋子的,现在按照马走日的规则,问在互不影响的情况下最多能放多少个马 题目分析:这里首先简单介绍一下二分图最大独立集的定义: 通俗来讲 ...

  5. 二分图相关结论及口胡证明

    考虑点集A,B二分图 最小点覆盖: 概念:用最少的点覆盖二分图中所有边. 结论:最小覆盖点=最大匹配 证明:选择点集A所有匹配点,如果还存在一条边的两个端点都不在匹配点中那么让该两点匹配则最大匹配数目 ...

  6. 图论 —— 最大团问题

    [问题描述] 当 G′ 是图 G 的子图,且 G′ 是关于 V′ 的完全图时,子图 G' 为图 G 的团:当 G' 是团,且不是其他团的子集时,G' 为图 G 的极大团:当 G' 是极大团时,且点数最 ...

  7. 图论:无向网(UDN)的定义及相关操作

    目录 创建无向网(邻接矩阵存储) 打印邻接矩阵 Prim算法得最小生成树 BFS DFS 示例 相关定义(队列将在BFS用到) #include <stdio.h> #include &l ...

  8. 回溯、图论——最大团问题(求最大完全子图)

     1.问题分析 要想解决最大团问题,也就是求最大完全子图.我们需要了解相关概念,现在有如下图: (1)完全子图: 给定无向图G=(V,E),其中V是顶点集,E是边集.G'=(V',E')如果顶点集V' ...

  9. 图论(二) 树与二分图

    无圈图:一个图的任何子图都不是圈 树:连通无圈图 树删除的任意一条边都会变成非连通图产品 对树中给定的两个结点的x,y,树中存在唯一一条XY路,因此此路为测地线 若树有Ñ个结点,对条边,则P = N- ...

最新文章

  1. vim+cscope+ctags打造属于自己的IDE
  2. UICollectionView自定义布局(二)
  3. 可恶,新网互联又出问题了
  4. python表示数字6_【Python 1-6】Python教程之——数字
  5. Linux进程休眠和唤醒
  6. 中国旅游日出游火爆 中国第一水乡游人突破5万
  7. 如何把定义的数组传回主函数_java数组如何定义
  8. eclipse光标变成黑块变粗解决办法
  9. python 写入excel_基于Python实现Excel的读写
  10. 一维转二维_Excel – 一维表和二维表相互转换,只要一个“=”搞定
  11. sql重命名数据库_为什么要为SQL单元测试巧妙地命名数据库对象
  12. 我的年终总结:做了9年SOC的一点点实践体会
  13. linux c 静态连接,Linux cmake 静态链接boost
  14. 使用Struts框架,实现用户登陆功能
  15. sql 左连接数据出现重复
  16. Excel-制作简单的环形柱状图
  17. 用双轨驶向未来:千兆宽带将如何改变我们的家庭生活?
  18. libjpeg实现YUV转jpeg
  19. 【演示文稿制作软件】Focusky教程 | 贯穿整个演示文稿背景音乐的添加与设置
  20. 微信小程序:setData 数据传输长度为 1678 KB,存在有性能问题!

热门文章

  1. GX Work2 三菱 FX-Q系列IP地址配置
  2. 180g6服务器支持显卡超频吗,显卡怎么超频?五种显卡超频方法随你挑!
  3. 点击元素,目标元素显示和隐藏。点击其他非指定区域,目标元素隐藏
  4. VMware虚拟机屏幕太小,解决方案(windows)
  5. 数据结构实验报告(六)
  6. Android SVG动画详细例子
  7. 计算机控制技术(于海生)-整理
  8. monkey runner工具
  9. 利用 jQuery 操作页面元素的方法,实现电商网站购物车页面商品数量的增加和减少操作,要求单项价格和总价随着数量的改变而改变
  10. 复旦FM17522芯片读写M1卡(S50/S70)、CPU卡要点摘录