判断图的连通性

上机系统的判分功能目前还没开放,所以以下所给代码仅供参考,并不能保证完全正确(自己分别测试了强连通,单向连通,弱连通,不连通的一个样例,都过了),等可以判分了,我会再进行相应修改或提示
所设计的程序能够通过编译,并能够实现从给定n个结点的图G的邻接矩阵A, 求出G的可达矩阵P,并判断图G的连通性(强连通:qlt、单向连通:dxlt、弱连通:rlt、不连通:blt)。

输入格式
首先输入结点个数n,然后输入图G的邻接矩阵A。
输出格式
输出其关联矩阵A1,A2,A3…An 和可达矩阵d,及其连通性。
样例输入
5
0 1 0 1 0
0 0 0 0 1
0 1 0 1 0
0 0 0 0 1
1 0 1 0 0
样例输出
b1:
0 0 0 0 2
1 0 1 0 0
0 0 0 0 2
1 0 1 0 0
0 2 0 2 0
b2:
2 0 2 0 0
0 2 0 2 0
2 0 2 0 0
0 2 0 2 0
0 0 0 0 4
b3:
0 4 0 4 0
0 0 0 0 4
0 4 0 4 0
0 0 0 0 4
4 0 4 0 0
b4:
0 0 0 0 8
4 0 4 0 0
0 0 0 0 8
4 0 4 0 0
0 8 0 8 0
b5:
8 0 8 0 0
0 8 0 8 0
8 0 8 0 0
0 8 0 8 0
0 0 0 0 16
P:
1 1 1 1 1
1 1 1 1 1
1 1 1 1 1
1 1 1 1 1
1 1 1 1 1
qlt
样例说明:
矩阵A关联矩阵为自身笛卡尔乘积。对于可达矩阵P来说,如果P的所有元素 均为1,则所给的有向图是强连通的;对于P的所有元素(除主对角线元素外)Pij来说,均有:Pij+Pji>0,则所给有向图是单向连通的。当所给有向图既不是强连通的,又不是单向连通的时候,我们改造邻接矩阵为:对于矩阵A中所有的元素(除主对角线的元素外)aij,若aij=1或aji=1,则令aij等于1且aji等于1。对于这样改造之后所得到的 新的矩阵A’(A’相当于原有向图忽略方向之后所得到的无向图的邻接矩阵),再用 前面所述的方法进行判断,当P’的所有元素(除主对角线的元素外)均为1时,原有向图是弱连通图;否则,原有向图是不连通的。

思路分析及求解过程说明
目的是要根据可达矩阵的不同来判断图的连通性,所以在最开始的部分我引用了之前写过的生成可达矩阵的代码,需要注意的是,要把每一次矩阵乘法的结果(即邻接矩阵的幂)输出,感谢这道题要输出这种过程量,才让我发现原来的代码有一点瑕疵:

for(i=0;i<n;i++)  
	{   
		for(j=0;j<n;j++)  
		{    
			for(k=0;k<n;k++)    
			{     
				c[i][j]+=a[i][k]*b[k][j];    
			}   
		}  
	}

问题出在c[i][j]+=a[i][k]*b[k][j]; 由于矩阵乘法的代码我是单独编的一个程序,然后再拿过来用的,所以在那里面只考虑了一次相乘的结果,而这里c[i][j]要多次使用,我在for循环里面却没有及时初始化c里面的元素(也就是说c并不是矩阵的高次幂,而是从二次到该循环所到次的所有幂的和),所以就需要在定义的函数里面将c中元素初始化为0,如果使用memset函数初始化会更简洁。(之前有关可达矩阵的博客上面的代码已进行了修正,并在评论区给出了提示)
第二个问题出在另一个数组d里面:

for(i=0;i<n;i++)
	{
		for(j=0;j<n;j++)
		{
			scanf("%d",&a[i][j]);
			b[i][j]=a[i][j];
			d[i][j]=a[i][j];/*注意经过下面的矩阵乘法后,d里面的值会从矩阵的二次幂开始
			增加,所以先把矩阵的一次幂赋给d,然后后面就不会遗漏了*/ 
		}
	}

最初我没有加d[i][j]=a[i][j];这一条语句,导致会遗漏邻接矩阵的一次幂。
将这两个问题解决后判断强连通就没有问题了,然后开始考虑单向连通:

for(i=0;i<n;i++)
{
	for(j=0;j<n;j++)
	{/*单向连通的判断,即两点之间至少有一点可以到达另一点
	转换成矩阵元素判断即为d[i][j]+d[j][i]>0*/ 
		if(d[i][j]+d[j][i]==0)
		{
			flag=1;//flag为标记量
		}
	}
}
if(flag==0)	
{//flag在遍历完d中元素后初值未变,说明单向连通 
	printf("dxlt");
}

在for循环的if里面可以考虑跳出循环,也可以像我一样修改标记量的值,我个人更倾向于后者吧,因为这样调试的时候查看一个变量就好了,可能会更方便一点,然后就只剩弱连通和不连通了。其实题目已经给说明了,也就是将有向图的邻接矩阵修改为其底图的邻接矩阵(必为对称阵),修改结束之后再将强连通判断之前的过程跑一遍,然后根据t的值进行判断即可。
最终代码如下:

#include<stdio.h>
#define N 100
int mult(int a[N][N],int b[N][N],int n,int c[N][N])
{
	int i,j,k;
	for(i=0;i<n;i++)
	{
		for(j=0;j<n;j++)
		{
			c[i][j]=0;
		}
	}
	for(i=0;i<n;i++)  
	{   
		for(j=0;j<n;j++)  
		{    
			for(k=0;k<n;k++)    
			{     
				c[i][j]+=a[i][k]*b[k][j];    
			}   
		}  
	}
}//定义实现矩阵乘法的函数 
int main()
{
	int n,i,j,k,a[N][N],b[N][N]={0},c[100][100]={0},d[100][100]={0},t=1,flag=0;
	scanf("%d",&n);
	for(i=0;i<n;i++)
	{
		for(j=0;j<n;j++)
		{
			scanf("%d",&a[i][j]);
			b[i][j]=a[i][j];
			d[i][j]=a[i][j];/*注意经过下面的矩阵乘法后,d里面的值会从矩阵的二次幂开始
			增加,所以先把矩阵的一次幂赋给d,然后后面就不会遗漏了*/ 
		}
	}
	for(i=0;i<n;i++)
	{
		mult(a,b,n,c);
		printf("b%d:\n",i+1);//按题目的输出格式来 
		for(j=0;j<n;j++)
		{
			for(k=0;k<n;k++)
			{
				printf("%d ",c[j][k]);
				b[j][k]=c[j][k];//用c来接收前两个矩阵相乘的结果,并将其储存在b中以实现求解高次矩阵 
				d[j][k]+=c[j][k];
			}
			printf("\n");
		}
	}
	for(i=0;i<n;i++)
	{
		for(j=0;j<n;j++)
		{
			if(i==j)
			{
				d[i][j]=1;
			}
			else if(i!=j&&d[i][j]>0)
			{
				d[i][j]=1;
			}
		}
	}//将d转化为可达矩阵 
	for(i=0;i<n;i++)
	{
		for(j=0;j<n;j++)
		{
			t*=d[i][j];
		}
	}//此处的t用来判断原图是否为强连通图,若是,则每一个元素均为1,不影响t的初值 
	printf("p:\n");
	for(i=0;i<n;i++)
	{
		for(j=0;j<n;j++)
		{
			printf("%d ",d[i][j]);
		}
		printf("\n");
	}//输出可达矩阵 
	if(t==1) printf("qlt");//强连通的条件,前面已有解释 
	else if(t==0)
	{//非强连通要再细分 
		for(i=0;i<n;i++)
		{
			for(j=0;j<n;j++)
			{/*单向连通的判断,即两点之间至少有一点可以到达另一点
			转换成矩阵元素判断即为d[i][j]+d[j][i]>0*/ 
				if(d[i][j]+d[j][i]==0)
				{
					flag=1;//flag为标记量,当不满足单向连通的条件时,修改标记量的值
				}
			}
		}
		if(flag==0)
		{//flag在遍历完d中元素后初值未变,说明单向连通 
			printf("dxlt");
		}
		else
		{//剩余两种情况的判断 
			for(i=0;i<n;i++)
			{
				for(j=0;j<n;j++)
				{
					if(i!=j&&a[i][j]==1)
					{
						a[j][i]=1;//先将a中元素按题干要求转换 
					}
					b[i][j]=a[i][j];//b进行相应地元素更新 
				}
			}
			for(i=0;i<n;i++)
			{
				mult(a,b,n,c);
				for(j=0;j<n;j++)
				{
					for(k=0;k<n;k++)
					{
						b[j][k]=c[j][k];/*用c来接收前两个矩阵相乘的结果,
						并将其储存在b中以实现求解高次矩阵*/
						d[j][k]+=c[j][k];
					}
				}
			}
			for(i=0;i<n;i++)
			{
				for(j=0;j<n;j++)
				{
					if(i==j)
					{
						d[i][j]=1;
					}
					else if(i!=j&&d[i][j]>0)
					{
						d[i][j]=1;
					}
				}
			}//重复强连通判断之前的过程 
			t=1;//注意在这个分支下t是0,要重新赋值为1 
			for(i=0;i<n;i++)
			{
				for(j=0;j<n;j++)
				{
					t*=d[i][j];
				}
			}//类似强连通判断之前的一个步骤			
			if(t==1) printf("rlt");
			else printf("blt");
		}
	} 
}
  • 4
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值