第五题啦。开始针对语言咧,用 Java 来搞会非常简单~
题目链接
http://poj.org/problem?id=1737
题目描述
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.
题目输入
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.
题目输出
For each test case output the answer on a single line.
样例输入
1 2 3 4 0
样例输出
1 1 4 38
解题思路
关键词:容斥原理,组合数,联通分量
计算过程中需要用到三个值:
- t o t a l n total_n totaln 由 n n n 个点组成的图一共有多少种。
- d i s c o n n e c t n disconnect_n disconnectn 由 n n n 个点组成且不联通的图一共有多少种。
- c o n n e c t n connect_n connectn 由 n n n 个点组成且联通的图一共有多少种。
先来思考 t o t a l n total_n totaln 如何计算。
包含 n n n 个节点的图,最多有 n ∗ ( n − 1 ) 2 \frac{n*(n-1)}{2} 2n∗(n−1) 条边。因无需考虑联调性,所以每条边都可有可无。因此总共可组成 2 n ∗ ( n − 1 ) 2 2^{\frac{n*(n-1)}{2}} 22n∗(n−1) 种不同的图,记为 t o t a l n total_n totaln。
再来考虑 d i s c o n n e c t n disconnect_n disconnectn 如何计算。
如果图是不联通的,则必有两个或两个以上的联通分量。不妨设第 n n n 个节点所在的联通分量有 i i i 个节点。我们从 n − 1 n-1 n−1 个节点中选取 i − 1 i-1 i−1 个节点,来和第 n n n 个点组成联通分量,因此总共有 C n − 1 i − 1 ∗ c o n n e c t i C_{n-1}^{i-1}*connect_{i} Cn−1i−1∗connecti 种联通分量。对于剩余的 n − i n-i n−i 个点的联通性无需关心,因此:
d i s c o n n e c t n = ∑ i = 1 n − 1 C n − 1 i − 1 c o n n e c t i t o t a l n − i disconnect_n = \sum_{i = 1}^{n-1}C_{n-1}^{i-1}\ connect_{i}\ total_{n-i} disconnectn=i=1∑n−1Cn−1i−1 connecti totaln−i
至于 c o n n e c t n connect_n connectn 利用容斥原理就很好求啦~
显然三者满足:
c
o
n
n
e
c
t
n
=
t
o
t
a
l
n
−
d
i
s
c
o
n
n
e
c
t
n
connect_n = total_n - disconnect_n
connectn=totaln−disconnectn
但是该题的答案巨大,超出64位整数的存储上限,因此对于大多数语言来说,选手需要手动实现大数的加减乘。
或者,偷懒用 Java 的 BigInteger 实验上述计算过程。
import java.math.BigInteger;
import java.util.Scanner;
public class Main {
static BigInteger quickPower(BigInteger v, int p) {
if (p == 0) {
return BigInteger.ONE;
}
if (p == 1) {
return v;
}
BigInteger vv = quickPower(v, p / 2);
return vv.multiply(vv).multiply(quickPower(v, p & 1));
}
public static void main(String[] args) {
BigInteger[] c = new BigInteger[51];
BigInteger[] d = new BigInteger[51];
BigInteger[] t = new BigInteger[51];
BigInteger[][] combine = new BigInteger[51][51];
for (int i = 0; i <= 50; i++) {
for (int j = 0; j <= i; j++) {
if (j == 0 || i == j) {
combine[i][j] = BigInteger.ONE;
} else {
combine[i][j] = BigInteger.ZERO.add(combine[i-1][j-1]).add(combine[i-1][j]);
}
}
}
for (int i = 1; i <= 50; i++) {
t[i] = quickPower(BigInteger.valueOf(2), i*(i-1)/2);
d[i] = BigInteger.ZERO;
for (int j = 1; j < i; j++) {
d[i] = d[i].add(combine[i-1][j-1].multiply(c[j]).multiply(t[i-j]));
}
c[i] = t[i].subtract(d[i]);
}
Scanner reader=new Scanner(System.in);
while (reader.hasNextInt()) {
int n = reader.nextInt();
if (n != 0) {
System.out.println(c[n]);
}
}
return;
}
}