【模板】矩阵树定理

参考题目:SPOJ HIGH

对了有一道假题目


解析:

先明确几个定义。

对于一个图 G G G,定义其度数矩阵为 D [ G ] D[G] D[G],为一个 n ∗ n n*n nn的矩阵,满足当 i ! = j i!=j i!=j的时候, d i , j = 0 d_{i,j}=0 di,j=0,而大对角线上的元素 d i , i d_{i,i} di,i值为节点 v i v_i vi的度数。

对于一个图 G G G,定义其邻接矩阵为 A [ G ] A[G] A[G],为一个 n ∗ n n*n nn的矩阵,满足元素 a i , j a_{i,j} ai,j等于 v i , v j v_i,v_j vi,vj之间直接连接的边数,也称为接数。

对于一个图 G G G,定义其 K i r c h o f f Kirchoff Kirchoff矩阵( C [ G ] C[G] C[G])如下,为一个 n ∗ n n*n nn的矩阵,满足对于 ∀ i , j \forall i,j i,j c i , j = d i , j − a i , j c_{i,j}=d_{i,j}-a_{i,j} ci,j=di,jai,j

K i r c h o f f   M a t r i x − T r e e Kirchoff\text{ }Matrix-Tree Kirchoff MatrixTree定理内容描述如下。

对于一个图 G G G,其生成树个数等于 d e t ( C [ G ] ) det(C[G]) det(C[G])。其中 d e t det det表示该矩阵行列式的值。

证明是个很妙妙的东西。
然而太长了不想写

那就主要讲一讲怎么 O ( n 3 ) O(n^3) O(n3)求一个矩阵的行列式吧。

首先行列式的定义要补充一下。

一个 n ∗ n n*n nn方阵 A A A的行列式记为 d e t ( A ) det(A) det(A) ∣ A ∣ |A| A

把一个元素 a i , j a_{i,j} ai,j所在行列划去后(不是置为0,而是整行整列消去),剩余的矩阵的行列式称为元素 a i , j a_{i,j} ai,j的余子式,记作 M i , j M_{i,j} Mi,j,而 A i , j = ( − 1 ) j + i M i , j A_{i,j}=(-1)^{j+i}M_{i,j} Ai,j=(1)j+iMi,j称作 a i , j a_{i,j} ai,j的代数余子式,而行列式的值定义如下 d e t ( A ) = ∑ a i , j ( − 1 ) i + j d e t ( A i , j ) det(A)=\sum a_{i,j}(-1)^{i+j}det(A_{i,j}) det(A)=ai,j(1)i+jdet(Ai,j)

好的上面这个完全不说人话的表述方式你看懂了吗?

如果不懂。。。就去其他地方先学习一下吧,反正我觉得上面这个表述得挺清楚的。

于是行列式就有一些奇奇怪怪的性质。
比如一行(列),加上或减去另一行(列)每个元素的相同倍数,行列式的值不变。(证明可能什么时候才会补吧)

那么我们就可以利用高斯消元将当前矩阵消成一个上三角矩阵,那么就可以轻易求出行列式的值了。

其行列式的值就变成了对角线上的所有值之积。
证明很简单,因为这时候其他地方的积的式子中都含有至少一个元素为0,那么有贡献的就只剩下这条大对角线了。


代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define re register
#define gc getchar
#define pc putchar
#define cs const

inline
int getint(){
	re int num=0;
	re char c;
	while(!isdigit(c=gc()));
	while(isdigit(c))num=(num<<1)+(num<<3)+(c^48),c=gc();
	return num;
}

double a[13][13];

inline
double gauss(int n){
	for(int re i=1;i<=n;++i){
		int pos=i;
		for(int re j=i+1;j<=n;++j){
			if(fabs(a[j][i])>fabs(a[pos][i]))pos=j;
		}
		if(fabs(a[pos][i])<=1e-6)return 0;
		if(pos!=i)swap(a[pos],a[i]);
		for(int re j=i+1;j<=n;++j){
			double tmp=a[j][i]/a[i][i];
			for(int re k=1;k<=n;++k){
				a[j][k]-=a[i][k]*tmp;
			}
		}
	}
	double ret=1;
	for(int re i=1;i<=n;++i){
		ret*=a[i][i];
	}
	return fabs(ret);
}

signed main(){
	int T=getint();
	while(T--){
		int n=getint(),m=getint();
		memset(a,0,sizeof a);
		while(m--){
			int u=getint(),v=getint();
			a[u][u]+=1,a[v][v]+=1;
			a[u][v]-=1,a[v][u]-=1;
		}
		printf("%.0lf\n",gauss(n-1));
	}
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
矩阵定理是一种基于图的行列式计算算法,可以用来判断两个图是否同构。下面是使用矩阵定理判断两个图是否同构的具体流程: 1. 对于两个图G1和G2,先计算它们的拉普拉斯矩阵L1和L2。 2. 对于拉普拉斯矩阵L1和L2,分别选择一个相同的大小的行列子矩阵,称为L1'和L2'。 3. 计算L1'和L2'的行列式,如果它们相等,则认为G1和G2同构。否则,认为它们不同构。 下面是使用C语言代码实现矩阵定理判断两个图是否同构的示例: ``` #include <stdio.h> #include <stdlib.h> #include <string.h> // 定义矩阵大小 #define MAX_N 100 #define MAX_M 5000 // 定义拉普拉斯矩阵 int L1[MAX_N][MAX_N], L2[MAX_N][MAX_N]; // 定义行列式计算函数 int determinant(int n, int mat[MAX_N][MAX_N]) { int det = 1; for (int i = 0; i < n; i++) { for (int j = i + 1; j < n; j++) { while (mat[j][i]) { int t = mat[i][i] / mat[j][i]; for (int k = i; k < n; k++) { mat[i][k] = (mat[i][k] - t * mat[j][k]); // 交换i,j两行 int temp; temp = mat[i][k]; mat[i][k] = mat[j][k]; mat[j][k] = temp; } det = -det; } } det *= mat[i][i]; } return det; } int main() { // 读入两个图 int n, m1, m2; scanf("%d%d%d", &n, &m1, &m2); memset(L1, 0, sizeof(L1)); memset(L2, 0, sizeof(L2)); for (int i = 0; i < m1; i++) { int u, v; scanf("%d%d", &u, &v); L1[u - 1][u - 1]++; L1[v - 1][v - 1]++; L1[u - 1][v - 1]--; L1[v - 1][u - 1]--; } for (int i = 0; i < m2; i++) { int u, v; scanf("%d%d", &u, &v); L2[u - 1][u - 1]++; L2[v - 1][v - 1]++; L2[u - 1][v - 1]--; L2[v - 1][u - 1]--; } // 计算行列式 int k = n / 2; int det1 = determinant(k, L1); int det2 = determinant(k, L2); // 判断是否同构 if (det1 == det2) { printf("Same\n"); } else { printf("Different\n"); } return 0; } ``` 在上面的代码中,我们先输入两个图的节点数、边数和边的信息,然后构造它们的拉普拉斯矩阵。接着,我们选择相同大小的行列子矩阵,用行列式计算函数计算它们的行列式,最后比较两个行列式是否相等,从而判断它们是否同构。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值