楼教主男人八题(第五题)

第五题啦。开始针对语言咧,用 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(n1) 条边。因无需考虑联调性,所以每条边都可有可无。因此总共可组成 2 n ∗ ( n − 1 ) 2 2^{\frac{n*(n-1)}{2}} 22n(n1) 种不同的图,记为 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 n1 个节点中选取 i − 1 i-1 i1 个节点,来和第 n n n 个点组成联通分量,因此总共有 C n − 1 i − 1 ∗ c o n n e c t i C_{n-1}^{i-1}*connect_{i} Cn1i1connecti 种联通分量。对于剩余的 n − i n-i ni 个点的联通性无需关心,因此:

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=1n1Cn1i1 connecti totalni

至于 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=totalndisconnectn

但是该题的答案巨大,超出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;
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值