破圈法求解最小生成树c语言实现(已验证)

时间:2022-04-23 16:20:06

破圈法求解最小生成树c语言实现(已验证)

下面是算法伪代码,每一个算法都取一个图作为输入,并返回一个边集T。

对该算法,证明T是一棵最小生成树,或者证明T不是一棵最小生成树。此外,对于每个算法,无论它是否能计算出一棵最小生成树,都要给出其最有效的实现。

MAYBE-MST-A(G,w)

Sort the edges into nonincreasing order of edge weights w

T<-E

For each edge e, taken in nonincreasing order by weight

Do if T-{e} is a connected graph

    Then T<-T-e

Return T

算法基本思想:

先将图G的边按照权的递减顺序排列后,依次检验每条边,在保持连通的情况下,每次删除最大权边,直到所有边都被遍历到无法删除任何一边(或余下n-1条边为止)。

证明:

生成树证明:

1、     如果给定连通图G没有回路,那么G本身就是一棵生成树

2、     如果G中只有一个回路,则删去G的回路上的一条边(不删除节点),则产生的图仍是连通的且没有回路,则得到的子图就是G的一棵生成树;

3、     如果G的回路不止一个,只要删去每一个回路上的一条边,知道G的子图是连通且没有回路且与图G有一样的结点集,那么这个子图就是一棵生成树。

4、     重复步骤3,则直到所有边都不能删除,由于删除判断条件,得到的子图就是一棵生成树。

MST证明:

若在某个回路C中有一条唯一的最长边,则T­­­*中一定不含这条边,因为优先删除回路中最长的边。

若在某个边e的割集中有一条唯一一条最短边,则T*中一定含有这条边(The Cut Property:考虑图G中的一条边e。如果存在一个cut(A,B),使得e是所有跨越该割的所有边中权重最小者,则e一定在G的MST中。

),且不含有其他边,因为一旦含有其他边就构成了回路(Lonely-Cut Corollary如果边e是跨越了cut(A, B)的唯一一条边,则e不可能在任一圈中。)。

反向证明:假设T*中跨越cut(A,B)的边不只一条,则在算法结束之前一定会遍历到其中的成圈的边(Double-Crossing),根据权值选取方法和删除圈的一边仍为连通图的条件,一定会将权值较大的边删除,直到无环且剩下的唯一一条边是最短边。

算法变量:

a[n][n]:带权图的邻接矩阵,a[i][j]=w或a[i][j]=0;

max:标记当前找到的准备删去的边的权值;

p:标记找到的要删去的权值所在的行号;

q:标记找到的要删去的权值所在的列好;

am:标记找到的最大元素(am是为了保护权值大但不能删的边),如果a[i][j]不能删除,则可以让a[p][q]=am,a[q][p]=am来还原刚才删去的边;

I,j:二维数组的行号和列号

sm:图的边数,每删除一个边,sm就减1,当sm=n-1时,结束

wt:最小生成树的权值和

算法实现(c语言)

 #include<stdio.h>
BY Yuanshuai Zheng,UESTC
#define n 5
int a[n][n];
int flag,am,p,q;
INPUT()
{
int i,j;
printf("输入图的带权邻接矩阵:\n");
for(i = ;i<n;i++)
{
for(j=;j<n;j++)
scanf("%d",&a[i][j]);
}
}
OUTPUT(int a[n][n])
{
int i,j;
for(i=;i<n;i++)
{
for(j=;j<n;j++)
printf("%5d",a[i][j]);
printf("\n");
}
}
MAX(int a1[n][n],int am1,int p1,int q1)
{
int i,j,ptm,qtm;
int max;
max = ;
for(i=;i<n;i++)
{
for(j=i;j<n;j++)
if((a1[i][j]>max)&&(a1[i][j]<=am1)&&((i!=p1)||(j!=q1)))
{
max = a1[i][j];
ptm = i;
qtm = j; }
}
am = max;
printf("max=%5d\t",am);
p = ptm;
q = qtm;
a[p][q]=;
a[q][p]=;
}
WSHALL(int array[n][n])
{
int i,j,k,m=;
int r[n][n],B[n][n];
for(i=;i<n;i++)
{
for(j=;j<n;j++)
{
r[i][j]=;
B[i][j]=array[i][j];
//分界线
if(array[i][j]>=)
B[i][j]=;
else
B[i][j]=;
//分界线
}
}
for(j=;j<n;j++)
{
for(i=;i<n;i++)
if(B[i][j]>=)
{
for(k=;k<n;k++)
{
if(B[k][i]>=)
{
B[k][j]=B[j][k]=;
}
}
}
}
for(i=;i<n;i++)
{
for(j=;j<n;j++)
if(!B[i][j])
{
return ;
}
}
return ;
} //方法1
// for(k=0;k<n;k++)
// B[i][j]=B[i][k]+B[j][k];
// }
// }
// for(i=0;i<n;i++)
// {
// for(j=0;j<n;j++)
// {
// r[i][j]=B[i][j];
// {
// if((r[i][j]>=1)||(i==j))
// m=m+1;
// }
// }
// }
// printf("m=%d\t",m);
// if(m==n*n)
// flag = 1;
// else
// flag=0;
// return(flag); int main()
{
int i,j,sm,wt=;
am = ,p=-,q=-,sm=;
INPUT();
for(i=;i<n;i++)
{
for(j=i;j<n;j++)
{
if(a[i][j]>)
sm = sm+;
}
}
printf("\nsm=%d\n",sm);
printf("输出图的带权邻接矩阵:\n");
OUTPUT(a);
printf("\n");
while(sm>n-)
{
MAX(a,am,p,q);
flag=WSHALL(a);//华沙尔算法判断是否连通
//printf("flag= %d",flag);
{
if(flag==)
{
sm=sm-;
//printf("flag= %d",flag);
}
else
{
a[p][q]=am;
a[q][p]=am;
}
}
}
for(i=;i<n;i++)
for(j=i;j<n;j++)
{
wt=wt+a[i][j];
}
printf("\n\n输出最小生成树的带权邻接矩阵:\n");
OUTPUT(a);
printf("最小生成树的树权是: %d\n",wt);
}

代码验证:

例子:

0

6

10

0

0

6

0

3

8

5

10

3

0

6

7

0

8

6

0

0

0

5

7

0

0

破圈法求解最小生成树c语言实现(已验证)