• 生成树计数问题


    【题目描述】

    给你一个n个点,m条边的无向图,求其生成树个数。(1<=n<=12)

    【分析】

    由于原问题规模较小,可以使用复杂度较高的算法,如指数级的动态规划。

    那么如果n=1000呢?

    【基尔霍夫矩阵】

    对于无向图,它的kirchhoff矩阵定义为度数矩阵减去邻接矩阵。

    在计算时,用a表示kirchhoff矩阵。则:

    当i==j时,a[i][j]=i的度数。

    当i!=j时,i到j有k条边相连时,a[i][j]=-k  (没有边相连为0)

    例如下图

    它的kirchhoff矩阵为

     

     1 for(int i=1;i<=m;i++)
     2     {
     3         int x=read(),y=read();
     4         map[x][y]=map[y][x]=1;
     5     }
     6     for(int i=1;i<=n;i++)
     7     {
     8         int d=0;
     9         for(int j=1;j<=n;j++)
    10             if(map[i][j])  d++;
    11         a[i][i]=d;
    12     }
    13     for(int i=1;i<=n;i++)
    14         for(int j=1;j<=n;j++)
    15             if(map[i][j])  a[i][j]=-1;

    【Matrix-Tree定理】

    对于一个无向图G,它的生成树个数等于其Kirchhoff矩阵任何一个n-1阶主子式的行列式的绝对值。
    所谓n-1阶主子式,就是对于任意一个r,将C的第r行和第r列同时删去后的新矩阵,用Cr表示。

    【行列式的计算】

    首先有几点性质:

    1、矩阵的某一行同时除以某个数,行列式的值不变。

    2、矩阵的某两行相交换,行列式的值取相反数。

    那么我们可以通过这些性质,把原矩阵化为三角形矩阵。则行列式的值就等于对角线上值得乘积。(从左上到右下的对角线)

     1 double sum=1;  
     2     for(int i=1;i<=n-1;i++)
     3     {
     4         if(a[i][i]==0)
     5         {
     6             int flag=0;
     7             for(int j=i+1;j<=n-1;j++)
     8                 if(a[j][i]!=0)
     9                 {
    10                     for(int k=i;k<=n-1;k++)  swap(a[i][k],a[j][k]);
    11                     flag=1;  break;
    12                 }
    13             if(!flag)  return 0;
    14         }
    15         sum*=a[i][i];
    16         for(int j=i+1;j<=n-1;j++)  a[i][j]/=a[i][i];
    17         for(int j=i+1;j<=n-1;j++)
    18             for(int k=i+1;k<=n-1;k++)
    19                 a[j][k]-=a[i][k]*a[j][i];
    20     }
    21     return sum;

    附spoj104代码:

    就是多组数据而已。。。

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cstring>
     4 #include<cstdlib>
     5 #include<cmath>
     6 #include<ctime>
     7 #include<algorithm>
     8 using namespace std;
     9 #define MAXN 101
    10 int n,m,T,map[MAXN][MAXN];  //map表示邻接矩阵,a表示基尔霍夫矩阵
    11 double a[MAXN][MAXN];
    12 namespace init
    13 {
    14     char buf[1<<15],*fs,*ft;
    15     inline char getc(){return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++;}
    16     inline int read()
    17     {
    18         int x=0,f=1;  char ch=getchar();
    19         while(!isdigit(ch))  {if(ch=='-')  f=-1;  ch=getchar();}
    20         while(isdigit(ch))  {x=x*10+ch-'0';  ch=getchar();}
    21         return x*f;
    22     }
    23 }using namespace init;
    24 double work()
    25 {
    26     double sum=1;  int sign=0;
    27     for(int i=1;i<=n-1;i++)
    28     {
    29         if(a[i][i]==0)
    30         {
    31             int flag=0;
    32             for(int j=i+1;j<=n-1;j++)
    33                 if(a[j][i]!=0)
    34                 {
    35                     for(int k=i;k<=n-1;k++)  swap(a[i][k],a[j][k]);
    36                     flag=1;  sign++;  break;
    37                 }
    38             if(!flag)  return 0;
    39         }
    40         sum*=a[i][i];
    41         for(int j=i+1;j<=n-1;j++)  a[i][j]/=a[i][i];
    42         for(int j=i+1;j<=n-1;j++)
    43             for(int k=i+1;k<=n-1;k++)
    44                 a[j][k]-=a[i][k]*a[j][i];
    45     }
    46     if(sign&1)  sum=-sum;
    47     return sum;
    48 }
    49 int main()
    50 {
    51     int T=read();
    52     while(T--)
    53     {
    54         memset(a,0,sizeof(a));
    55         memset(map,0,sizeof(map));
    56         n=read();  m=read();
    57         for(int i=1;i<=m;i++)
    58         {
    59             int x=read(),y=read();
    60             map[x][y]=map[y][x]=1;
    61         }
    62         for(int i=1;i<=n;i++)
    63         {
    64             int d=0;
    65             for(int j=1;j<=n;j++)
    66                 if(map[i][j])  d++;
    67             a[i][i]=d;
    68         }
    69         for(int i=1;i<=n;i++)
    70             for(int j=1;j<=n;j++)
    71                 if(map[i][j])  a[i][j]=-1;
    72         double ans=work();
    73         printf("%0.0lf
    ",ans);
    74     }
    75     return 0;
    76 }
  • 相关阅读:
    MySQL DDL 在指定位置新增字段
    .NET平台常见技术框架整理汇总
    使用Linq求和方法Sum计算集合中多个元素和时应该注意的性能问题
    python时间操作
    django 利用原生sql操作数据库
    滑动验证码获取像素偏移量
    python opencv简单使用
    django通过旧数据库库生成models
    pandas 操作csv文件
    简单工厂模式(Simple Factory Pattern)
  • 原文地址:https://www.cnblogs.com/chty/p/5868327.html
Copyright © 2020-2023  润新知