上机系统的判分功能目前还没开放,所以以下所给代码仅供参考,并不能保证完全正确(自己分别测试了强连通,单向连通,弱连通,不连通的一个样例,都过了),等可以判分了,我会再进行相应修改或提示
所设计的程序能够通过编译,并能够实现从给定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");
}
}
}