二分图的几个概念及代码,最小生成树的复杂度,寻找欧拉路径的算法及代码
1.几个原来不是很清楚的概念:
二分图的最大独立点集: 在所有顶点中选一些点,使得这个点集内两两之间无连线,那么这个点集就叫做独立点集,而顶点数最多的那个集合就是最大独立集。
最小点覆盖: 选取一部分点,使得这些点能够覆盖图中所有的边。满足所选点的个数最小的点即为最小点覆盖。
定理:二分图的最大匹配=最小点覆盖
推论:最大独立集=节点数 - 最大匹配。
模板:(顶点编号从1开始)
bool dfs(int u)
{
for(int i=0;i<G[u].size();i++){
int v=G[u][i];
if(!used[v]){
used[v]=1;
if(linked[v]==-1||dfs(linked[v])){
linked[v]=u;
return 1;
}
}
}
return 0;
}
int solve()
{
memset(linked,-1,sizeof(linked));
int u,res=0;
for(u=1;u<=n;u++){
memset(used,0,sizeof(used));
if(dfs(u)) res++;
}
return res;
}
2.关于最小生成树算法的复杂度:
prim算法:时间复杂度O(n^2),空间复杂度O(n).
Kruskal算法:时间复杂度O(mlogm) ,空间复杂度O(m).
所以对于完全图的话,选用prim算法比较OK!
3.关于欧拉路径的算法:
1.朴素的求欧拉路径的算法: 时间复杂度比较高!!!
关键代码:
void dfs(int v)
{
for(int i=fa[v];i!=-1;i=e[i].next){
int id=e[i].id,u=e[i].u;
if(!used[id]){
used[id]=1;
dfs(u);
ans[++h]=id;
}
}
}
2.fleury算法: 比较高效的欧拉路径: 搜索效率比较高!!!
关键代码:
void dfs(int u,int dx)
{
for(int i=0;i<G[u].size();i++){
int id=G[u][i].SD,v=G[u][i].FT;//id记录这条边的编号,v为这条边的要到达的另一个节点
G[u][i] = G[u][G[u].size()-1];
G[u].erase(G[u].end()-1);
dfs(v,id);
i--;
}
stk.push(dx);//如果要记录的是节点顺序,改为stk.push(u)即可!
}
反正我开始是没看懂,后来手动模拟了一下,主要是要理解那个
stk.push(dx);
每次搜索完后,直接删除这个节点,删除的方式比较特别,先用最后一个数赋覆盖当前搜索的数,然后直接删除最 后一个数,这样就比较优雅的删除了最后一个数。然后呢,就是去理解这个最后的过程了,建议手动模拟,每次把当前的状态压入堆栈,来模拟一下,或者自己输出中间过程看看实现过程。
最后再次贡献一个比较能理解的版本(代码量比较大!!),其实就是吧原来那个stk.push(dx)的过程弄的特别复杂了而已!
关键代码:
void dfs(int x){
int i;
s.top++;
s.node[s.top]=x;
for(i=0;i<n;i++){
if(Edge[i][x]>0){
Edge[i][x]=0;
Edge[x][i]=0;
dfs(i);
break;
}
}
}
void Fleury(int x){
int i,b;
s.top=0;s.node[s.top]=x;
while(s.top>=0){
b=0;
for(i=0;i<n;i++){
if(Edge[s.node[s.top]][i]>0){
b=1;break;
}
}
if(b==0){
printf("%d ",s.node[s.top]+1);
s.top--;
}
else{
s.top--;
dfs(s.node[s.top+1]);
}
}
printf("\n");
}
最后一点分析: 为什么朴素的寻找欧拉路径的算法会明显慢与fluery算法???
仔细点看不难发现,朴素的寻找欧拉路径的算法,在递归寻找节点继续搜索时,会遍历所有与之相连的边,然后判断是否这条边时候已经被走过,但是fluery算法不同之处就在于它把已经搜索过的节点都删除掉了,下次再来遍历这个节点的时候循环次数会减1,从而达到了缩短搜索时间的目的!
转载注明出处,谢谢!