poj 1737

参考:http://hi.baidu.com/billdu/blog/item/64e40ccf429cfc37b600c809.html

http://www.cnblogs.com/longdouhzt/archive/2012/03/05/2380994.html

题目大意,就是给出结点个数,这些结点都认为是不一样的,要你求出这些点能组成多少种无向图的全连通图

组合数学的知识:

f(n)表示n个结点共有多少种方案。

n个结点的总方案数为2^C(n,2);

然后排除不连通的情况,设与结点1连通的点数为k个,这k个点从n-1个点中选取,而包含1的k+1个结点是个连通图,所以有C(n-1, k) * f(k+1)方案

而剩下的n-k-1个结点各点之间任意连接方案数为2^C(n-k-1, 2),所以总的不连通的方案数为C(n-1, k) * f(k+1) * 2^C(n-k-1, 2)

所以f(n) = 2^C(n,2) -sum( C(n-1, k) * f(k+1) * 2^C(n-k-1, 2));      0 <=k < n

这里求C(n,k)用到公式C(n,k) = C(n-1, k) + C(n-1, k-1)
#include <iostream>
#include <cstdio>

using namespace std;

const int base = 10000;
const int maxn = 1000;

int n, c[51][51][maxn], a[51][maxn], pr[1230][maxn];

void repair(int *a);
void let(int *a, int val);
void output(int *a);
void add(int *a, int *b);
void mul(int *a, int *b);
void mul(int *a, int val);
void sub(int *a, int *b);
void copy(int *a, int *b);

int main()
{
    int t[maxn];
    let(pr[0], 1);
    for(int i = 1; i < 1230; i++)
    {
        copy(pr[i], pr[i-1]);
        mul(pr[i], 2);
    }
    let(c[0][0], 1);
    
    for(int i = 1; i <= 50; i++)
    {
        let(c[i][0], 1);
        let(c[i][i], 1);
        for(int j = 1; j < i; j++)
        {
            let(c[i][j], 0);
            add(c[i][j], c[i-1][j]);
            add(c[i][j], c[i-1][j-1]);
        }
    }
    let(a[1], 1);
    let(a[2], 1);
    let(a[3], 4);
    for(int i = 4; i <= 50; i++)
    {
        copy(a[i], pr[i * (i - 1) / 2]);
        for(int j = 1; j < i; j++)
        {
            copy(t, a[j]);
            mul(t, c[i-1][j-1]);
            mul(t, pr[(i - j) * (i - j - 1) / 2]);
            sub(a[i], t);
        }
    }
    
    while(true)
    {
        scanf("%d", &n);
        if(n == 0)
            break;
        output(a[n]);
        printf("\n");
    }
    return 0;
}

void repair(int *a)
{
    for(int i = 1; i <= a[0]; i++)
    {
        if(a[i] >= base)
        {
            if(i == a[0])
                a[0]++;
            a[i+1] += a[i] / base;
            a[i] %= base;
        }
        else if(a[i] < 0)
        {
            while(a[i] < 0)
            {
                a[i+1]--;
                a[i] += base;
            }
        }
    }
    
    for(int i = a[0]; i >= 2; i--)
    {
        if(a[i] == 0)
            a[0]--;
        else
            break;
    }
}

void let(int *a, int val)
{
    a[0] = 1;
    a[1] = val;
    for(int i = 2; i < maxn; i++)
        a[i] = 0;
    repair(a);
}

void output(int *a)
{
    printf("%d", a[a[0]]);
    for (int i = a[0] - 1; i >= 1; i--)
        printf("%04d", a[i]);
}

void add(int *a, int *b)
{
    if(a[0] < b[0])
        a[0] = b[0];
    for(int i = 1; i <= b[0]; i++)
        a[i] += b[i];
    repair(a);
}

void sub(int *a, int *b)
{
    for(int i = 1; i <= b[0]; i++)
        a[i] -= b[i];
    repair(a);
}

void copy(int *a, int *b)
{
    for(int i = 0; i <= b[0]; i++)
        a[i] = b[i];
}

void mul(int *a, int *b)
{
    int tmp[maxn];
    let(tmp, 0);
    for(int i = 1; i <= b[0]; i++)
    {
        for(int j = 1; j <= a[0]; j++)
            tmp[i + j - 1] += b[i] * a[j];
        tmp[0] = i * a[0];
        repair(tmp);
    }
    copy(a, tmp);
}

void mul(int *a, int val)
{
    for(int i = 1; i <= a[0]; i++)
        a[i] *= val;
    repair(a);
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值