poj1737 Connected Graph(n点无向连通图)

本文介绍了一种计算给定点数的无向连通图数量的方法,提出了两种不同的递推公式,并给出了详细的代码实现过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目链接

题目描述:
给出n个点,求n个点的无向连通图

分析:
巨坑啊

经典题目,有两种方法:

总方案数-不合法方案

n n 个点的完全图有C(n,2)=n(n1)2条边,显然就有 2C(n,2) 2 C ( n , 2 ) 种子图(枚举每条边是否选择)

f[i] f [ i ] 表示每个点都和点1相连的连通图的个数
假设1号点所在的连通块大小为 i i
那么和1连通的这i1个点就有 C(n1,i1) C ( n − 1 , i − 1 ) 种选择,方案数为 C(n1,i1)f[i] C ( n − 1 , i − 1 ) ∗ f [ i ]

其它 ni n − i 个点间任意连边即可,方案数为 2C(ni,2) 2 C ( n − i , 2 )

则有递推公式:

f[n]=2C(n,2)i=1n1f[i]C(n1,i1)2C(ni,2) f [ n ] = 2 C ( n , 2 ) − ∑ i = 1 n − 1 f [ i ] ∗ C ( n − 1 , i − 1 ) ∗ 2 C ( n − i , 2 )

在代码实现上,基本上都是高精度
所以网上的代码都是什么Java,python之类的

直接求方案数

考虑去掉1号点时,2号点的情况
设此时2所在的连通块共有 i i 个点
i个点和剩下的 ni n − i 个点分别处在两个不同连通块中(下面就要把这两个连通块连接在一起)
其方案数为 f[i],f[ni] f [ i ] , f [ n − i ] ,2号点需要在剩下的点(除去点1,点2)中选择 i1 i − 1 个点同处一个连通块,方案数为 C(n2,i1) C ( n − 2 , i − 1 )
而这 i i 个点与除去1的ni1个点(这 ni1 n − i − 1 个点都与点1连通)都需要通过1号点来连接
所以与2号点连通的 i i 个点与1号点至少有1条边连接,方案数为2i1
故这样的情况总共有: f[i]f[ni]C(n2,i1)(2i1) f [ i ] ∗ f [ n − i ] ∗ C ( n − 2 , i − 1 ) ∗ ( 2 i − 1 )

则有递推公式

f[n]=i=1n1f[i]f[ni]C(n2,i1)(2i1) f [ n ] = ∑ i = 1 n − 1 f [ i ] ∗ f [ n − i ] ∗ C ( n − 2 , i − 1 ) ∗ ( 2 i − 1 )

这种思路xue微有点困难,而且还是需要用高精度
不过只需要高精加和高精乘即可(比第一种方法简单一点)

tip

方法二code

#include<cstdio>
#include<iostream>
#include<cstring>
#define ll long long

using namespace std;

int n,nn=2;
struct node{
    int o[400],len;

    node operator *(const node &a) const {
        int L=(len+a.len);
        node ans; ans.clear();
        ans.len=L;
        for (int i=1;i<=len;i++)
            for (int j=1;j<=a.len;j++) {
                ans.o[i+j-1]+=o[i]*a.o[j];
                ans.o[i+j]+=ans.o[i+j-1]/10;
                ans.o[i+j-1]%=10;
            }
        while (!ans.o[ans.len]) ans.len--;
        return ans;
    }

    node operator +(const node &a) const {
        int L=max(len,a.len);
        int d=0;
        node ans; ans.clear();
        for (int i=1;i<=L;i++) {
            ans.o[i]=o[i]+a.o[i]+d;
            d=ans.o[i]/10;
            ans.o[i]%=10;
        }
        ans.len=L;
        if (d) ans.o[++ans.len]=d;
        return ans;    
    }

    void clear() {memset(o,0,sizeof(o));len=0;}

    node get(ll x) {
        node ans; ans.clear();
        while (x) {
            ans.o[++ans.len]=x%10;
            x/=10;
        }
        return ans;
    }
};
node f[51],c[51][51];

void prepare() {
    c[0][0].len=1; c[0][0].o[1]=1;
    for (int i=1;i<=50;i++) {
        c[i][0].o[1]=1; c[i][0].len=1;
        for (int j=1;j<=i;j++) 
            c[i][j]=c[i-1][j]+c[i-1][j-1]; 
    }
}

void solve(int n) {
    node t; 
    for (int i=1;i<n;i++) {
        t=t.get((1LL<<i)-1); 
        f[n]=f[n]+f[i]*f[n-i]*c[n-2][i-1]*t;
    }
}

void print(int n) {
    for (int i=f[n].len;i>=1;i--)
        printf("%d",f[n].o[i]);
    printf("\n");
}

int main()
{
    f[1].o[1]=1; f[1].len=1;
    f[2].o[1]=1; f[2].len=1;
    nn=2;
    prepare();
    while (scanf("%d",&n)!=EOF&&n) {
        if (n<=nn) {print(n);continue;}
        for (int i=nn+1;i<=n;i++) 
            solve(i);
        nn=n;
        print(n);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值