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 }
接下来是多校联盟中有关于本题的拓展:
问题:求左侧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) } }$