ccf-csp 201312-4 有趣的数(Java)

1 篇文章 0 订阅


官网传送门

问题描述

我们把一个数称为有趣的,当且仅当:
  1. 它的数字只包含0, 1, 2, 3,且这四个数字都出现过至少一次。
  2. 所有的0都出现在所有的1之前,而所有的2都出现在所有的3之前。
  3. 最高位数字不为0。
  因此,符合我们定义的最小的有趣的数是2013。除此以外,4位的有趣的数还有两个:2031和2301。
  请计算恰好有n位的有趣的数的个数。由于答案可能非常大,只需要输出答案除以1000000007的余数。

格式要求

输入格式
  输入只有一行,包括恰好一个正整数n (4 ≤ n ≤ 1000)。
输出格式
  输出只有一行,包括恰好n 位的整数中有趣的数的个数除以1000000007的余数。
样例输入
4
样例输出
3

解题思路

  • 首先我们可以知道开头数字肯定是2
  • 对于n位的数字,假定其中包含p个0和1,q个2和3,其中   p , q ≥ 2   \,p,q\ge2\, p,q2
  • 为这p个0和1挑选位置,有   C n − 1 p   \,C_{n-1}^{p}\, Cn1p种情况,因为首位置肯定是2,所以只有n-1个位置供挑选
  • 接下来再确定p中0和1各有几个,0的数目可以为1到p-1,所以共p-1种情况,对于每一种情况直接将0放到选中的p的空位的前面,1放到空位的后面即可
  • 对于q-1个2和3(不算首位的2),2的数目可以为0到q-2,所以共q-1种情况
  • 所以共有   ∑ p = 2 n − 2 ( p − 1 ) ∗ ( n − p − 1 )   \,\sum\limits_{p=2}^{n-2}(p-1)*(n-p-1)\, p=2n2(p1)(np1)种情况,编写代码求该公式即可

AC代码

import java.math.BigInteger;
import java.util.Scanner;

public class Main {

	static final int NUM = 1000000007;
	
	public static void main(String[] args) {
		Scanner in = new Scanner(System.in);
		int n = in.nextInt();
		in.close();
		System.out.println(getAnswer(n));
	}

	//排列组合法求解
	public static BigInteger getAnswer(int n) {
		BigInteger result = BigInteger.valueOf(0);
		for (int i = 2; i <= n - 2; i++) {
			result = result.add(c(n-1, i).multiply(BigInteger.valueOf((i - 1) * (n - 1 - i))));
		}
		return result.mod(BigInteger.valueOf(NUM));
	}
	
	//组合计算函数
	public static BigInteger c(int n, int k) {
		if (n - k < k)
			k = n - k;
		
		//组合数分子部分,即n*(n-1)*...*(n-k+1)
		BigInteger numerator = BigInteger.valueOf(1);
		//组合数分母部分,即1*2*...*k
		BigInteger denominator = BigInteger.valueOf(1);
		
		for (int i = 0; i < k; i++) {
			numerator = numerator.multiply(BigInteger.valueOf(n-i));
			denominator = denominator.multiply(BigInteger.valueOf(i+1));
		}
		return numerator.divide(denominator);
	}
}

注意事项

  • 观察下面一段组合函数求解代码:
	public static BigInteger c(int n, int k) {
		if (n - k < k)
			k = n - k;
		
		BigInteger result = BigInteger.valueOf(1);
		
		for (int i = 0; i < k; i++) {
			result = result.multiply(BigInteger.valueOf(n-i)).divide(BigInteger.valueOf(i+1));
		}
		return result;
	}

看上去好像没有什么问题,甚至比AC代码中的还要简洁,本人就是在这里卡了好长时间,苦死宝宝了,我还是太菜,一直就没往它身上想。
其中result.multiply(BigInteger.valueOf(n-i)).divide(BigInteger.valueOf(i+1))不一定是整数,所以如果采用上面这种写法的话会有精度损失,而且每一步的损失都叠加在了一起,最后一顿冥思苦想不得其解。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值