您的位置:首页 > 其它

poj 1737 Connected Graph - 容斥原理 - 动态规划

2018-02-05 15:33 399 查看
Description

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.



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.
Sample Input

1
2
3
4
0

Sample Output

1
1
4
38

Source

LouTiancheng@POJ

题目大意

  有$n$个互不相同的点,问它们组成的无向连通图的个数。

  这一类问题(比如什么连通欧拉图计数,强连通图计数)的常用套路:容斥 + 动归(递推)

  直接求并不好求,而且容易出现重复计数的情况。

  用$f_{n}$表示我们想要的答案,$g_{n}$表示$n$个点组成的无向图的个数。

  显然有$g_{n} = 2^{C_{n}^{2}}$,考虑如何通过$g$来求$f$

  我们希望只有1个连通块,所以1号点所在的连通块的大小应为$n$,那么用$g_{n}$减去一号点所在连通块大小不是$n$的图的个数就是$f_{n}$了。

  考虑枚举1号点所在连通块的大小为$i$,那么删掉后这个连通块后可以得到一个大小为$(n - i)$的无向图,由于点和点之间互不相同,所以还要考虑选出来的$(i - 1)$的个点的方案数。

  所以得到由$g$算$f$的式子:

$f_{n} = g_{n} - \sum_{i = 1}^{n - 1}f_{i}\cdot g_{n - i}\cdot C_{n - 1} ^ {i - 1}$

  又因为poj不良心,所以还要写个高精度。

Code

/**
* poj
* Problem#1737
* Accepted
* Time: 32ms
* Memory: 984k
*/
#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
typedef bool boolean;
#define ll long long

typedef class BigInteger {
public:
int w[800];

BigInteger() {        }
BigInteger(ll x) {
memset(w, 0, sizeof(w));
while(x) {
w[++w[0]] = x % 10;
x /= 10;
}
}

void clear() {
memset(w, 0, sizeof(w));
}

BigInteger operator + (BigInteger b) {
BigInteger rt;
rt.clear();
rt[0] = max(b[0], w[0]) + 1;
for (int i = 1; i <= rt[0]; i++) {
rt[i] += w[i] + b[i];
rt[i + 1] += rt[i] / 10;
rt[i] %= 10;
}
while (rt[0] && rt[rt[0]] == 0)    rt[0]--;
return rt;
}

BigInteger operator - (BigInteger b) {
BigInteger rt;
rt.clear();
rt[0] = w[0];
for (int i = 1; i <= w[0]; i++) {
rt[i] += w[i] - b[i];
if (rt[i] < 0)
rt[i + 1]--, rt[i] += 10;
}
while (rt[0] && rt[rt[0]] == 0)    rt[0]--;
return rt;
}

BigInteger operator * (BigInteger b) {
BigInteger rt;
rt.clear();
rt[0] = b[0] + w[0];
for (int i = 1; i <= w[0]; i++)
for (int j = 1; j <= b[0]; j++) {
rt[i + j - 1] += w[i] * b[j];
rt[i + j] += rt[i + j - 1] / 10;
rt[i + j - 1] %= 10;
}
while (rt[0] && rt[rt[0]] == 0)    rt[0]--;
return rt;
}

int& operator [] (int pos) {
return w[pos];
}

void print() {
for (int i = w[0]; i; i--)
putchar(w[i] + '0');
putchar('\n');
}
}BigIntegwe;

const int N = 55;
int n;
BigInteger f
, g
;
ll C

;

BigInteger qpow(BigInteger a, ll pos) {
BigInteger rt = 1, pa = a;
for (; pos; pos >>= 1, pa = pa * pa)
if (pos & 1)
rt = rt * pa;
return rt;
}

inline void prepare() {
C[0][0] = 1;
for (int i = 1; i <= 50; i++) {
C[i][0] = C[i][i] = 1;
for (int j = 1; j < i; j++)
C[i][j] = C[i - 1][j - 1] + C[i - 1][j];
}

for (int i = 1; i <= 50; i++) {
f[i] = g[i] = qpow(2, C[i][2]);
for (int j = 1; j < i; j++)
f[i] = f[i] - f[j] * g[i - j] * C[i - 1][j - 1];
}
}

inline boolean init() {
scanf("%d", &n);
return n != 0;
}

inline void solve() {
f
.print();
}

int main() {
prepare();
while(init()) {
solve();
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: