POJ - 1737 Connected Graph【计数DP】

An undirected graph is a set V of vertices and a set of E∈{V*V} edges.An undirected graph is connected if and only if for every pair (u,v) of vertices,u is reachable from v.
You are to write a program that tries to calculate the number of different connected undirected graph with n vertices.
For example,there are 4 different connected undirected graphs with 3 vertices.
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Y1SWOMPS-1600437560685)(https://odzkskevi.qnssl.com/6cf5433dd1e316c2dbfbb67f3c18c209?v=1531073224)]

Input

The input contains several test cases. Each test case contains an integer n, denoting the number of vertices. You may assume that 1<=n<=50. The last test case is followed by one zero.

Output

For each test case output the answer on a single line.


题目大意

求N个节点的无向连通图有多少个,每个结点视为互有不同,1<=n<=50


题目分析

直接求可行方案不是很方便,所以试着反过来
N个节点的无向连通图总数=N个节点的无向图总数-N个节点的无向非连通图总数

d p [ i ] dp[i] dp[i]表示i个节点的无向连通图总数

i个节点的无向图
由于其边总数至多有 i ∗ ( i − 1 ) / 2 i*(i-1)/2 i(i1)/2
每条边有存在与不存在两种情况
所以i个节点的无向连通图总数为 2 i ∗ ( i − 1 ) / 2 2^{i*(i-1)/2} 2i(i1)/2

i个节点的无向图非连通图
试枚举包含结点1的连通块大小为j
显然这个连通块有 C i − 1 j − 1 C_{i-1}^{j-1} Ci1j1种选法
那么这个连通块可能的情况总数就有 d p [ j ] ∗ C i − 1 j − 1 dp[j]*C_{i-1}^{j-1} dp[j]Ci1j1
除该连通块外剩余 i − j i-j ij个节点,共有 2 ( i − j ) ∗ ( i − j − 1 ) / 2 2^{(i-j)*(i-j-1)/2} 2(ij)(ij1)/2种组合
得i个节点的无向图非连通图总数为 ∑ j = 1 i − 1 d p [ j ] ∗ C i − 1 j − 1 ∗ 2 ( i − j ) ∗ ( i − j − 1 ) / 2 \sum_{j=1}^{i-1}dp[j]*C_{i-1}^{j-1}*2^{(i-j)*(i-j-1)/2} j=1i1dp[j]Ci1j12(ij)(ij1)/2

最后根据上述推导得递推式
d p [ 1 ] = 1 dp[1]=1 dp[1]=1
d p [ i ] = 2 i ∗ ( i − 1 ) / 2 − ∑ j = 1 i − 1 d p [ j ] ∗ C i − 1 j − 1 ∗ 2 ( i − j ) ∗ ( i − j − 1 ) / 2 dp[i]=2^{i*(i-1)/2}-\sum_{j=1}^{i-1}dp[j]*C_{i-1}^{j-1}*2^{(i-j)*(i-j-1)/2} dp[i]=2i(i1)/2j=1i1dp[j]Ci1j12(ij)(ij1)/2

另外数据很大,要写高精
高精调的气死人+_+


#include<iostream>
#include<map>
#include<algorithm>
#include<queue>
#include<cstring>
#include<cstdio>
using namespace std;

int read()
{
    int f=1,x=0;
    char ss=getchar();
    while(ss<'0'||ss>'9'){if(ss=='-')f=-1;ss=getchar();}
    while(ss>='0'&&ss<='9'){x=x*10+ss-'0';ss=getchar();}
    return f*x;
}

struct bign
{
    int d[2010];
    int len;
    bign(){ memset(d,0,sizeof(d)); len=0;}
};

const int maxn=55;
int n;
bign C[maxn][maxn];
bign dp[maxn];

bign add(bign a,bign b)
{
    bign c;
    int carry=0;
    for(int i=0;i<a.len||i<b.len;i++)
    {
        int num=a.d[i]+b.d[i]+carry;
        c.d[c.len++]=num%10;
        carry=num/10;
    }
    if(carry!=0) c.d[c.len++]=carry;
    return c;
}

bign mul(bign a,bign b)
{
	bign c; int carry=0;
	if((a.len==1&&a.d[0]==0)||(b.len==1&&b.d[0]==0)){ c.len=1; return c;}
	for(int i=0;i<a.len;++i)
	for(int j=0;j<b.len;++j)
	{
		c.d[i+j]+=a.d[i]*b.d[j];
		if(c.d[i+j]!=0) carry=i+j;
	}
	
	for(int i=0;i<=carry+7;++i)
	{
		c.d[i+1]+=c.d[i]/10;
		c.d[i]%=10;
		if(c.d[i]!=0) c.len=i+1;
	}
	return c;
}

bign qpow(int k)
{
	bign a,c;
	c.len=1; c.d[0]=1;
	a.len=1; a.d[0]=2;
	
	while(k>0)
	{
		if(k&1) c=mul(c,a);
		a=mul(a,a);
		k>>=1;
	}
	return c;
}

bign sub(bign a,bign b)
{
	bign c;
	
	for(int i=0;i<a.len||i<b.len;++i)
	{
		if(a.d[i]<b.d[i]){ a.d[i+1]--; a.d[i]+=10;}
		c.d[c.len++]=a.d[i]-b.d[i];
	}
	while(c.len-1>=1&&c.d[c.len-1]==0) c.len--;
	return c;
}

void print(bign c)
{
	for(int i=c.len-1;i>=0;--i)
	printf("%d",c.d[i]);
}

int main()
{
    for(int i=1;i<=50;++i)
    {
    	C[i][0].len=1; C[i][0].d[0]=1;
    	C[i][i].len=1; C[i][i].d[0]=1;
    	for(int j=1;j<i;++j)
    	C[i][j]=add(C[i-1][j],C[i-1][j-1]);
	}
    
	dp[1].len=1; dp[1].d[0]=1;
	for(int i=2;i<=50;++i)
	{
		bign c;
		dp[i]=qpow(i*(i-1)>>1);
		for(int j=1;j<i;++j)
		c=add( c, mul( dp[j], mul( C[i-1][j-1], qpow((i-j)*(i-j-1)>>1) ) ) );
		dp[i]=sub(dp[i],c);
	}
	
	while(scanf("%d",&n)!=EOF)
	{
		if(n==0)break;
		print(dp[n]);printf("\n");
	}
	return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值