组合数问题

组合数问题简明的表示就是从n个物体中选出m个,一共有多少种选择,表达式为:


我们还可以找到他的递推表达式:


我们发现这个递推公式和杨辉三角的递推公式相同,唯一的区别就是没有最前面的那列1,他的表示形式为:


那么我们就可以利用这个递推公式来求解一些简单的组合数问题

栗子

栗子来源:https://www.cnblogs.com/Lanly/p/6126173.html

题目大意

告诉你组合数公式,其中n!=1*2*3*4*5*...*n;意思是从n个物体取出m个物体的方案数

现给定n、m、k,问在所有i(1<=i<=n),所有j(1<=j<=min(i,m))的(i,j)满足Cji是k的倍数的个数。

输入样例:
2 5  (两个数,第一个数t表示该数据有t组询问,第二个为k,接下来t行分别为n,m)
4 5  
6 7
输出样例:
0
7

数据范围:1<=n,m<=2000,1<=t<=10000,1<=k<=21

这样一看,然后我们又得到了他的递推公式,再一看这个范围的话,我们直接打表做就好,然后统计一下可以被整除的个数就好(因为没有交,所以只是测试样例对了,如果有错误,欢迎大犇指正!)

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>

using namespace std;
const int MAXN = 2500;
int num[MAXN][MAXN],a[MAXN][MAXN];
int main()
{
    int T,k;
    int n,m;
    memset(a,0,sizeof(a));
    memset(num,0,sizeof(num));
    scanf("%d%d",&T,&k);
    a[1][1] = 1%k;   //打表
    if(a[1][1] == 0) num[1][1] = 1;
    for(int i = 2;i <= 2001;i ++)
    {
        a[i][1] = i % k; if(a[i][1] == 0) num[i][1] = 1;
        for(int j = 2;j <= i;j ++)
        {
            a[i][j] = (a[i-1][j-1] + a[i-1][j]) % k;
            if(a[i][j] == 0)
                num[i][j] = num[i][j-1] + 1;
            else
                num[i][j] = num[i][j-1];
        }
    }
    while(T--)
    {
        scanf("%d%d",&n,&m);
        int ans = 0;
        for(int i = 1;i <= n;i ++)
            ans += num[i][min(i,m)];
        printf("%d\n",ans);
    }
    return 0;
}

上面只是一个简单的应用,下面是一个推导的题目:HDU 4927

题目意思

一个数列:我们需要对他进行n-1次操作,每次都是a[i] = a[i+1] - a[i],直到最后只剩下一个元素,将这个元素输出

先是模拟了一遍,交上去wa,后来检查了一下,模拟没错啊,那就是精度的问题,换成JAVA的BigInteger,还是wa,那这个题就没有想象的那么简单了,最后还是看了一下题解。发现我们直接可以根据输入直接求出我们最终的答案,那么他们每个之前的系数是什么呢?就是(n-1)^(n-1)的系数,并且数组中最后一个数对应这里面系数的最后一个

我们也会发现,(n-1)^(n-1)中的系数也满足杨辉三角里面的数字,所以我们也就可以直接向求解组合数那样来求解每一个前面的系数了

我们可以发现,我们分子是由(n-1 ~ 1)这样向后乘,分母是由(1 ~ n-1)向前乘的,所以这个题的复杂度我们就可以降为O(n),然后再加上我们的JAVA大数操作就可以了

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


public class Main{
	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		BigInteger [] a = new BigInteger[3007];
		int t;
		t = sc.nextInt();
		while(t > 0) {
			t --;
			int n = sc.nextInt();
			for(int i = 1;i <= n;i ++)
				a[i] = sc.nextBigInteger();
			BigInteger ans = BigInteger.ZERO;
			BigInteger temp = BigInteger.ONE;
			for(int i = 1;i <= n;i ++) 
			{
				BigInteger t1 = a[n-i+1];
				if(i != 1) temp = temp.multiply(BigInteger.valueOf(n-i+1));//向后乘 5*4....
				if(i != 1) temp = temp.divide(BigInteger.valueOf(i-1));//1*2*3.....
				t1 = t1.multiply(temp);
				if(i % 2 == 0)
					t1 = t1.multiply(BigInteger.valueOf(-1));
				ans = ans.add(t1);	
			}
			System.out.println(ans);
		}
	}
}

参考博客

https://blog.csdn.net/xzxxzx401/article/details/54973707

https://www.cnblogs.com/Lanly/p/6126173.html

猜你喜欢

转载自blog.csdn.net/li1615882553/article/details/80031405