實驗題目:關聯矩陣、相鄰矩陣、生成樹、環路空間、斷集空間的求解
實驗目的:
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 */
