POJ 1737 经典DP

问题:求含有n个点的连通图的个数。

解:

  考虑DP,$f(n)$表示n个点,每个点都和点1相连,且n个点互相连通的图的个数。

  (蓝字非常重要,这个条件有效地避免了重复计算

       $g(n)$表示n个点,每个点都和点1相连,且不是n个点互相连通的图的个数。

       $S(n)$表示n个点的图的个数。

显然,有:$f(n) = S(n)-g(n)$

     $S(n) = 2^{n(n-1)/2}$

而且有(关键):$g(n) = sum_{i=1}^{n-1}{C_{n-1}^{i-1} * f(i) * S(n-i)}$

  从除了1之外的n-1个点中选出i-1个点,让这i个点互相连通,而剩下的n-i个点和这i个点没有边相连,互相之间随意连接。

当然,博主并不想写高精度

 1 #include <iostream>
 2 #include <cstring>
 3 #include <algorithm>
 4 
 5 #define LL long long
 6 #define N 61
 7 
 8 using namespace std;
 9 
10 LL f[N],g[N];
11 LL C[N][N];
12 const int n=50;
13 
14 LL S(int x){
15     if(x==0) return 0;
16     return (1LL<<( (x*(x-1)) /2));
17 }
18 
19 int main(){
20     f[1]=1; g[1]=0;
21     C[0][0]=1;
22     for(int i=1;i<=n;i++){
23         C[i][0]=1;
24         for(int j=1;j<=i;j++)
25             C[i][j]=C[i-1][j-1]+C[i-1][j];
26     }
27     for(int i=2;i<=n;i++){
28         g[i]=0;
29         for(int j=1;j<i;j++)
30             g[i] = g[i] + (C[i-1][j-1]*f[j]*S(i-j));
31         f[i]=S(i)-g[i];
32     }
33     int x;
34     while(cin>>x,x) cout<<f[x]<<endl;
35     return 0;
36 }
View Code

接下来是多校联盟中有关于本题的拓展:

问题:求左侧n个点,右侧m个点的联通二分图个数

解:参照上面的解法。

  $f(i,j)$表示左面i个点右面j个点,每个点都和左面的点1相连,且n个点互相连通的图的个数。

  $g(i,j)$表示左面i个点右面j个点,每个点都和左面的点1相连,且n个点不是互相连通的图的个数。

  $S(i,j)$定义类比上面。

 和上面一样的,有:

$f(n,m)=S(n,m)-g(n,m)$

$S(n,m)=2^{nm}$

$g(n,m)=sum_{r=1}^{n-1}{ sum_{s=1}^{m-1}{ C_{n-1}^{r-1}*C_{m}^{s}*f(r,s)*S(n-r,m-s)    } }$

原文地址:https://www.cnblogs.com/lawyer/p/5700968.html