矩阵树定理

bzoj4031 小z的房间

题目大意:给定n*m的网格,有一些障碍点,求生成树的个数。

思路:矩阵树定理直接求解就可以了。但是因为要取模所以要稍微修改一下高斯消元,行列式交换行或列的时候行列式的值要变号。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define p 1000000000
#define LL long long
#define maxm 105
using namespace std;
char map[maxm][maxm];
int dx[4]={1,0,-1,0},dy[4]={0,1,0,-1},bi[maxm][maxm]={0};
LL ai[maxm][maxm]={0};
char in(){
    char ch;
    while(scanf("%c",&ch)==1){
        if (ch=='.'||ch=='*') return ch;
    }
}
int work(int n){
    int i,j;LL x,y,k,ans=1,f=1,t;
    for (i=1;i<=n;++i)
      for (j=1;j<=n;++j) ai[i][j]=(ai[i][j]+p)%p;
    for (i=1;i<=n;++i){
        for (j=i+1;j<=n;++j){
            x=ai[i][i];y=ai[j][i];
            while(y!=0){
                k=x/y;x%=y;swap(x,y);f=-f;
                for (t=i;t<=n;++t) 
                  ai[i][t]=(ai[i][t]-k*ai[j][t]%p+p)%p;
                for (t=i;t<=n;++t) swap(ai[i][t],ai[j][t]);
            }
        }if (!ai[i][i]) return 0;
        ans=ans*ai[i][i]%p;
    }return (int)(ans*f%p+p)%p;
}
int main(){
    int n,m,i,j,k,x,y,tot=0,u,v;scanf("%d%d",&n,&m);
    for (i=1;i<=n;++i){
        for (j=1;j<=m;++j){map[i][j]=in();if (map[i][j]=='.') bi[i][j]=++tot;}
    }for (i=1;i<=n;++i)
      for (j=1;j<=m;++j){
          if (map[i][j]!='.') continue;
          for (k=0;k<4;++k){
              x=i+dx[k];y=j+dy[k];
              if (x<1||x>n||y<1||y>m||map[x][y]!='.') continue;
              u=bi[i][j];v=bi[x][y];
              ++ai[u][u];--ai[u][v];
          }
      }printf("%d\n",work(tot-1));
}
View Code

 

bzoj3534 重建(!!!

题目大意:给定n个城市和他们之间边通行概率,求通行道路恰是生成树的概率。

思路:设矩阵树的数组是G,G[i][j]=Pe/(1-Pe),G[i][i]=-sigma G[i][j],ci=∏(1-Pe),所以G的n-1阶主子式*tmp就是答案了。

其实原来矩阵树定理中邻接矩阵的1的意义可以看做是概率,这里的是Pe,但并不能简单的改为Pe,而是Pe/(1-Pe),因为可以发现这样G的主子式是sigma(T)∏(e∈T)Pe/(1-Pe),乘上ci之后,不是树边的相当于乘了(1-Pe),是树边的相当于乘了Pe正好是我们想要(一棵生成树的概率就是树边概率*(1-非树边概率))的答案。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define N 55
#define eps 1e-9
#define LD double
using namespace std;
LD ai[N][N]={0.},ci;int n;
int cmp(LD x,LD y){
    if (x-y>eps) return 1;
    if (y-x>eps) return -1;
    return 0;}
LD gauss(){
    int i,j,k;LD x;--n;
    for (i=1;i<=n;++i){
        if (cmp(ai[i][i],0.)==0)
            for (j=i+1;j<=n;++j)
                if (cmp(ai[j][i],0.)!=0){
                    for (k=1;k<=n;++k) swap(ai[i][k],ai[j][k]);
                    break;}
        for (j=i+1;j<=n;++j){
            if (cmp(ai[j][i],0.)==0) continue;
            x=ai[j][i]/ai[i][i];
            for (k=i;k<=n;++k) ai[j][k]-=ai[i][k]*x;
        }
    }for (x=1.,i=1;i<=n;++i) x*=ai[i][i];
    return fabs(x);}
int main(){
    int i,j;scanf("%d",&n);
    for (ci=1.,i=1;i<=n;++i)
      for (j=1;j<=n;++j){
          scanf("%lf",&ai[i][j]);
          if (i==j) continue;
          if (ai[i][j]>1.-eps) ai[i][j]-=eps;
          if (i<j) ci*=1.-ai[i][j];
          ai[i][j]/=1.-ai[i][j];
      }for (i=1;i<=n;++i)
        for (j=1;j<=n;++j)
          if (i!=j) ai[i][i]-=ai[i][j];
    printf("%.10f\n",gauss()*ci);
}
View Code

 

转载于:https://www.cnblogs.com/Rivendell/p/4856173.html

矩阵定理是一种基于图的行列式计算算法,可以用来判断两个图是否同构。下面是使用矩阵定理判断两个图是否同构的具体流程: 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、付费专栏及课程。

余额充值