实验题目:关联矩阵、相邻矩阵、生成树、环路空间、断集空间的求解
实验目的:
1、掌握无向连通图生成树的求解方法;
2、掌握基本回路系统和环路空间的求解方法;
3、掌握基本割集系统和断集空间的求解方法;
4、了解生成树、环路空间和断集空间的实际应用。
实验要求:
1、给定无向简单连通图的相邻矩阵 (例如:)。
1、输出此图的关联矩阵M。
2、求此图所有生成树个数。
3、输出其中任意一颗生成树的相邻矩阵(默认第i行对应顶点vi)和关联矩阵(默认第i行对应顶点vi,第j列对应边ej)。
4、求此生成树对应的基本回路系统(输出形式如:{e1e4e3,e2e5e3})。
5、求此生成树对应的环路空间(输出形式如:{,e1e4e3,e2e5e3,e1e4e5e2})。
6、求此生成树对应的基本割集系统(输出形式如:{{e1,e4},{e2,e5},{e3,e4,e5}})。
7、求此生成树对应的断集空间(输出形式如:{, {e1,e4}, {e2,e5}, {e3,e4,e5}, {e1,e2,e4,e5}, {e1,e3,e5}, {e2,e3,e4}, {e1,e2,e3}})。
*说明:要求学生设计的程序不仅对给定相邻矩阵得出正确结果,还要对教师测试数据集得出正确结果。
实验内容和实验步骤:(由学生填写)
输入:第一行一个整数n,代表有n阶
接下来n行每行n个整数为邻接矩阵
输出:输出该图每条边连接的两个点
输出该图中的关联矩阵
输出生成树的个数
输出一棵生成树的相邻矩阵
输出一棵生成树的关联矩阵
输出生成树的基本回路系统
输出生成树的环路空间
输出生成树的基本割集系统
输出生成树的断集空间
1.求给定邻接矩阵的关联矩阵,遍历邻接矩阵中各点,若为a[i][j]==1则申请一条边连通i,j,将其加入到关联矩阵中,同时将第m条边连接的两点记录到edge【m】【1】,edge【m】【2】中为后面使用铺垫。在代码中trans()函数实现。
2.求生成树个数,参考网络上基尔霍夫矩阵,A-B=C,A代表度数方程,B代表邻接矩阵,C代表基尔霍夫矩阵。矩阵树定理为将基尔霍夫矩阵去掉一行一列后求得的行列式结果为生成树个数。在代码中countt()函数实现。
3.找一个最小生成树。用克鲁斯卡尔并查集的方式,首先将每个点fa赋值为自己,然后遍历每条边,如果连接的两点fa不同,说明不在同一个块上,可以连边。如果两点fa相同,说明两点在同一棵树上,继续连边的话会破坏树形结构。在确定连边的过程中可以同时计算生成树的邻接矩阵,同时对每条边记录scedge【m】【1】和scedge【m】【2】,m为生成树上的边。在代码中sctree()函数实现。
4.找基本回路系统,对于不在生成树上的边,先将该边加入一条基本回路中,然后对于该边连接的两点x和y,使用之前预备好的edge数组可以很容易的得到。然后在树上dfs查找从x到y的一条路径,该路径是存在且唯一的。对于每条弦,重复以上操作。在代码中basecircle()函数实现。
5.求环路空间。环路空间由基本回路和其环合运算组成。
在上一步中我们已经求出基本回路系统,并得到矩阵,第1行表示第一条基本回路由第一三四条边组成。那么对两条边环合运算实际上可以看成是判断同一条边是否重否出现奇数次,若是奇数次则可以保留。于是使用val数组来进行记录累计值。选择拿些边罗列所有情况是一件困难的是,容易知道对L条边每条边选或不选有两种情况,那么一共有2的L次种情况,及sx种情况。从0到sx-1的每个数字恰好能表示一种选择状态,其二进制的每一位为0或1恰好可以表示对应的基本回路选或不选。于是可以得到所有可能。
6.求基本割集系统。使用并查集的思想,对于树上的一条边i,对树上除去i的边连接的两点做并查集的合并,最后该树会被分为两部分。然后再对于弦,看其连接的两点x,y是否在同一块上,即他们的fa是否相等。如果不在则可加入到该基本割集中。对于树上的每条边做重复操作,最终生成基本割集系统。
7.断集空间。用基本割集做环合运算,过程与以上从4到5的过程相似。

#include<cstdio> #include<cstring> #include<cmath> #include<iostream> #include<algorithm> using namespace std; int n,m,a[1005][1005],b[1005][10005],edge[100][5],scedge[100][5]; int aa[105][105],bb[105][1005],fa[105],que[105],vis[105],v[105],ok,mm,xm; int l,huanlu[105][105],duanji[105][105],cal[105]; void trans()//转为关联矩阵 { for(int i=1;i<=n;i++) { for(int j=1;j<=i;j++) { if(a[i][j]==1) { m++; b[i][m]=1; b[j][m]=1; edge[m][1]=i; edge[m][2]=j; printf("e%d连接v%d和v%d\n",m,j,i); } } } mm=m; printf("\n关联矩阵为:\n"); for(int i=1;i<=n;i++) { for(int j=1;j<=m;j++) printf("%d ",b[i][j]); printf("\n"); } printf("\n"); return ; } int countt()//基尔霍夫矩阵 { memset(b,0,sizeof(b)); for(int i=1;i<=n;i++) for(int j=i+1;j<=n;j++) if(a[i][j] == 1) { b[i][i] ++;b[i][j] = -1; b[j][j] ++;b[j][i] = -1; } int res = 1;//计算行列式的值 for (int i=2;i<=n;i++)//去掉第一行 { for (int j=i+1;j<=n;j++) { while (b[j][i]) { int t = b[i][i] / b[j][i]; for (int k=i;k<=n;k++) b[i][k] -= b[j][k] * t; for (int k=i;k<=n;k++) swap(b[i][k], b[j][k]); res = -res; } } if (b[i][i] == 0) return 0; res = res * b[i][i]; } return abs(res); } int get(int x) { if(x==fa[x])return x; return fa[x]=get(fa[x]); } void merge(int i, int j) { fa[get(i)] = get(j); } void sctree()//生成树 { for(int i=1;i<+n;i++)fa[i]=i; for(int i=1;i<=m;i++) { int x=get(edge[i][1]); int y=get(edge[i][2]); if(x==y)continue; fa[x]=y; aa[edge[i][1]][edge[i][2]]=1; aa[edge[i][2]][edge[i][1]]=1; } printf("\n其中一棵生成树的相邻矩阵为:\n"); for(int i=1;i<=n;i++) { for(int j=1;j<=n;j++) printf("%d ",aa[i][j]); printf("\n"); } m=0; for(int i=1;i<=n;i++) for(int j=1;j<=i;j++) if(aa[i][j]==1) { m++; bb[i][m]=1; bb[j][m]=1; for(int k=1;k<=mm;k++) { int x=edge[k][1]; int y=edge[k][2]; if(x==i&&y==j){scedge[m][0]=k;edge[k][0]=1;break;} if(x==j&&y==i){scedge[m][0]=k;edge[k][0]=1;break;} } scedge[m][1]=i; scedge[m][2]=j; } printf("其中一棵生成树的关联矩阵为:\n"); for(int i=1;i<=n;i++) { for(int j=1;j<=m;j++) printf("%d ",bb[i][j]); printf("\n"); } } int search(int x,int y)//找连接x和y的是哪条边 { for(int i=1;i<=m;i++) { if(edge[i][1]==x&&edge[i][2]==y)return i; if(edge[i][2]==x&&edge[i][1]==y)return i; } } void dfs(int x,int y) { for(int i=1;i<=n;i++) { xm=search(x,i); if(aa[x][i]==1&&v[xm]==0) { vis[i]++; que[0]++; que[que[0]]=xm; v[xm]=1; if(i==y) { ok=1; return ; } dfs(i,y); if(ok==1)return ; vis[i]=0; que[que[0]]=0; que[0]--; } } } void basecircle()//基本回路系统 { int x,y,one=0; printf("\n基本回路系统:{"); for(int i=1;i<=mm;i++) { memset(vis,0,sizeof(vis));ok=0; memset(que,0,sizeof(que)); memset(v,0,sizeof(v)); x=edge[i][1]; y=edge[i][2]; if(aa[x][y]==1)continue; que[0]++;que[1]=i; dfs(x,y); l++; if(one==0)one=1; else printf(","); for(int j=1;j<=que[0];j++) { printf("e%d",que[j]); huanlu[l][que[j]]=1; } } printf("}\n\n"); for(int i=1;i<=l;i++) { for(int j=1;j<=mm;j++) { printf("%d ",huanlu[i][j]); } printf("\n"); } } void circleroad()//环路空间 { printf("\n环路空间:{空"); int sx=pow(2,l); for(int i=1;i<sx;i++) { memset(cal,0,sizeof(cal)); int fakei=i; for(int j=1;j<=l;j++) { if(fakei%2!=0) { for(int k=1;k<=mm;k++) cal[k]+=huanlu[j][k]; } fakei>>=1; } printf(","); for(int k=1;k<=mm;k++) if(cal[k]!=0&&cal[k]%2!=0)printf("e%d",k); } printf("}\n"); } void geji()//基本割集系统 { l=0; int one=0; printf("\n基本割集系统:{"); for(int i=1;i<=m;i++) { l++; if(one==0){printf("{e%d",scedge[i][0]);one=1;} else printf(",{e%d",scedge[i][0]); duanji[l][scedge[i][0]]=1; for(int j=1;j<=n;j++) fa[j]=j; for(int j=1;j<=m;j++) { if(j==i)continue; else merge(scedge[j][1],scedge[j][2]); } for(int j=1;j<=mm;j++) if(get(edge[j][1])!=get(edge[j][2])&&edge[j][0]==0){printf(",e%d",j);duanji[l][j]=1;} printf("}"); } printf("}\n"); } void duan() { printf("\n断集空间:{空"); int sx=pow(2,l); int one,oone=0; for(int i=1;i<sx;i++) { printf(",{"); one=0; memset(cal,0,sizeof(cal)); int fakei=i; for(int j=1;j<=l;j++) { if(fakei%2!=0) { for(int k=1;k<=mm;k++) cal[k]+=duanji[j][k]; } fakei>>=1; } for(int k=1;k<=mm;k++) if(cal[k]!=0&&cal[k]%2!=0) { if(one==0){printf("e%d",k);one=1;} else printf(",e%d",k); } printf("}"); } printf("}\n"); } int main() { printf("请输入阶数:"); scanf("%d",&n); for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) scanf("%d",&a[i][j]); trans(); printf("生成树个数为%d\n",countt()); sctree(); basecircle(); circleroad(); geji(); duan(); } /* 4 0 1 1 1 1 0 0 1 1 0 0 1 1 1 1 0 */