破圈法定义
破圈算法是1975年由我国数学家管梅谷教授提出来的。其基本思想是在给定的图中任意找出一个环路,删去该环路中权最大的边,然后在余下的图中再任意找出一个回路,再删去这个新找出的回路中权最大的边,……,一直重复上述过程,直到剩余的图中没有回路。剩余图便是最小生成树。
基本思路
破圈,顾名思义就是找到圈然后破开,直到整个图没有圈就是生成树,破圈法的主要注意事项有两个
寻圈
寻找回路是破圈法的基础,在生成生成树的过程中,用到破圈法时需要先找到回路,然后将回路断开,不断地对图的每一个点进行操作,直至整个图没有回路,这个图就变成了一个生成树
寻边
寻边就是指在破圈的过程中,对每一个寻圈寻找到的回路的边搜索最大值,当破圈行为发生时,直接破除最大值的边。
循环
当对某个点,不断地进行寻边,破圈,需要对这个图进行及时的更新,当破圈的事件发生的时候,下一次寻边,需要在上一次破圈行为发生的基础上进行,也就是说,破圈之后要对原图及时更新
程序框图
这里按照如下的图为例
由于是无向图,不难看出上图是错误的(因为懒得删除了),我们看下面的图片
不难看出这个图有很多的回路,如下是其临界矩阵的表达方式
邻接矩阵 | 1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|---|
1 | 0 | 1 | 20 | 0 | 0 |
2 | 1 | 0 | 6 | 4 | 7 |
3 | 20 | 6 | 0 | 9 | 2 |
4 | 0 | 4 | 9 | 0 | 100 |
5 | 0 | 7 | 2 | 100 | 0 |
操作步骤
深搜查圈
我们把起始点设置为第一个点,把顺序按照1 2 3 4 5深 的顺序进行搜索,对于每一次搜索,当经过某个点并且这个点不是起始点的时候,就把他进行标记,防止重复搜索,当搜索有发生到起始点的时候,证明深搜搜索到了回路,其s深搜演示图如下图所示ter)显然,第四次s搜的过程中,以一个点就搜索到了起始点1,代表着此图搜索到了回路,搜索过程如下图所示:
搜索过程
最大值破圈
对于搜索到的第一个圈而言,我们需要找到权重最大的边进行破圈,并且更新无向图,这样才能使下面的广搜不受影响
很明显,第一次深搜就搜索到了回路,接下来执行破圈,在搜索的过程中记录权重最大的边的坐标,然后将其删除,删除之后的无向图如下所示
重新深搜查圈
对于节点gen,当其上一次查圈成功并且破圈之后,在上一圈的基础上重新对该节点进行查圈破圈操作,重复上述操作,既可以得到最小生成树
再上图更新权重的基础上,以节点1重新进行搜索,很明显并没有回路,程序跳转,对下一个节点(也就是二进行深搜)
不停的重复上诉操作三个,由默认顺序可以得到,如果代码正确,其破圈顺序应该如下所示
基本变量与函数传参
基本变量
下面进行对变量的阐明
#define MIN 0 代表着不可达或者无路
int find_x; int find_y; 最大值的坐标
int drive[6][6]={0}; 代表邻接矩阵
int getd[6]={0}; 代表着点是否被访问到,其中1代表已经访问,0代表尚未访问 注:对于根节点而言他总是被访问到的
函数传参
void initial_getd() 初始化点达到数组为未达到
void create ( ) 初始化邻接矩阵
void bfs_search(int first,int tempf,int gen,int tempx,int tempy) 广搜代码,其中
参数 | 意义 |
---|---|
first | 代表着当前节点 |
tempf | 代表着上一个节点 |
gen | 代表着根节点 |
tempx | 用于调试的x坐标代码,无实际意义 |
tempy | 用于调试的y坐标代码,无实际意义 |
如上是代码的核心算法
全部代码
下面展示全部代码(对于某些部位附上注释)
#include<bits/stdc++.h>
using namespace std;
#define MAX 114514
#define MIN 0 //代表不可达
bool find;
int find_x;
int find_y;
int find_temp_x;
int find_temp_y;
int drive[6][6]={0};
int getd[6]={0};
void initial_getd()
{
for(int i=1;i<=5;i++)
{
getd[i]=0;
}
}
void create ()
{
int temp;
cin>>temp;
for(int i=0;i<temp;i++)
{
int j,k;
cin>>j>>k;
cin>>drive[j][k];
drive[k][j]=drive[j][k];
}
for(int i=1;i<=5;i++)
{
for(int j=1;j<=5;j++)
{
cout<<" "<<drive[i][j];
}
cout<<endl;
}
}
void dfs_search(int first,int tempf,int gen,int tempx,int tempy)
{
int right_point=gen;
getd[gen]=1;
for(int j=1;j<=5;j++)
{
if(drive[first][j]!=0&&j==gen&&j!=tempf&&first!=j)//找到根节点
{
if(drive[first][j]>=drive[find_x][find_y])
{
find_x=first;
find_y=j;
}
if(j==gen) //当搜到结点的时候,相当于找到回路,进行破圈操作
{
cout<<endl;
drive[find_x][find_y]=0;
drive[find_y][find_x]=0;
for(int i=1;i<=5;i++)
{
for(int j=1;j<=5;j++)
{
cout<<" "<<drive[i][j];
}
cout<<endl;
}
cout<<endl;
initial_getd();
ddfs_search(right_point,right_point,right_point,right_point,right_point);//从新调用广搜
return ;//结束下列搜索
}
}
if(drive[first][j]!=0&&getd[j]==0&&j!=tempf)//三个条件同时满足分别为 1.有路,2未达,3不是回到上一个节点
{
if(drive[first][j]>=drive[find_x][find_y])//如果全重大就更新
{
find_x=first;
find_y=j;
}
find_temp_x=first;
find_temp_y=j;
getd[j]=1;
getd[first]=1;
dfs_search(j,first,gen,find_temp_x,find_temp_y);//搜索下一个点
getd[j]=0;//初始化
if(first!=gen)//防止根节点被设为未达
getd[first]=0;
}
}
}
int main (void)
{
create();
for(int i=1;i<=5;i++)
{
cout<<getd[i]<<endl;
}
for(int i=1;i<=5;i++)
{
dfs_search(i,i,i,i,i);
find_x=1;
find_y=1;
}
for(int i=1;i<=5;i++)
{
for(int j=1;j<=5;j++)
{
cout<<" "<<drive[i][j];
}
cout<<endl;
}
cout<<endl<<endl;
system("pause");
}
//测试案例
/*
8
1 2 1
1 3 20
2 3 6
2 4 4
2 5 7
3 5 2
3 4 9
4 5 100
*/
运行截图
结果正确!
总结
其实搜索过程也可以用广搜解决
如有错误,希望指正谢谢