小Q的歌单(2)

1 题目

(此题来源于牛客网腾讯2018秋招笔试真题)

1.1 问题描述

小q有x首长为A的不同的歌和长为y首长度为B的不同的歌,从种选取任意首,组成长度为k的歌单,每首歌只能被选取一次,不考虑歌单内歌曲的先后顺序,问有多少种组成方法。

1.2 输入描述

第一行是一个整数,表示歌单的总长度为K。

第二行有四个整数,A(<=10)、X(<=100)、B(<=10)、Y(<=100),分别代表第一种长度和数量,第二种长度和数量,保证A!=B。

1.3 输出描述

输出为一个整数,表示组成方式有多少种,若输出结果过大,则对该数进行1000000007(中间8个0)取模的结果作为最终结果。

1.4 输入示例

5

2 3 3 3

1.5 输出示例

9

2 问题分析

第一种歌的长度为A,一共有X首,假设从里面选区m首,则有C1(m,X)种选法,同理第二种歌的长度为B,一共有Y首,假设从里面选区n首,则一共有C2(n,Y)种选法,然后将C1和C2进行相乘,结果则为最终结果(若太大则进行取模)。

这是一道典型的排列组合问题中的排列问题,如果不考虑计算量和超出整型范围问题,则直接可以通过公式计算得出最终的结果。但是,假设第一种歌有100首,从里面取出50首,进行组合计算则是C(50,100),这个数明显太大。

对数据进行分析,若为1首歌,有选0首和选1首两种状态;若为2首歌,有选0首、选1首、选2首三种状态,对应的种类分别为1 2 1;若为3首歌,有选0首、1首、2首、3首,对应的种类为1 3 3 1,以此类推。。。观察种类组成结构:

1 1

1 2 1

1 3 3 1

1 4 6 4 1

.。。。

若将最上面一排按上1,则刚好满足杨辉三角,第n行(从第0行开始)的第m个数刚好是C(m,n),C(m,n)刚好等于左上角C(m-1,n-1)和正上方C(m,n-1),第一列除外,第一列全部为1。若用二维数组表示则c[n][m] = c[n-1][m-1] + c[n-1][m]。这种方法增加了一定的空间消耗,大大降低了时间复杂度和计算复杂度。

3 代码编写

#include <iostream>
#include <stdio.h>
using namespace std;

int** init(const int len) {
	int** brr = new int*[len];
	for (int i = 0; i < len; i++)
		brr[i] = new int[len];
	//for (int i = 0; i < len; i++) 
	//	for (int j = 0; j < len; j++) 
	//		brr[i][j] = 0;				  //将数组初始化为0
	brr[0][0] = 1;
	brr[0][1] = 0;						
	//要从brr[1][1]开始计算,而第一行第2个数用到brr[0][0]和brr[0][1]
	for (int i = 1; i < len; i++) {
		brr[i][0] = 1;                    //每一行的第一个元素都初始化为1
		for (int j = 1; j < len; j++) {
			brr[i][j] = brr[i - 1][j - 1] + brr[i - 1][j];
			//第i行第j列个元素等于左上角加上正上方元素
		}
	}
	return brr;
}

int getmod(int a) {                           //取模函数
	const int mod = 1000000007;           //mod
	while (a > mod)
		a -= mod;
	return a;
}

void test() {
	int K;				      //组成歌单的总长度
	int A, X, B, Y;			      //第1种歌的长度和种类,第2种歌的长度和种类
	long long ans = 0;
	//_CRT_SECURE_NO_WARNINGS
	scanf("%d", &K);
	scanf("%d%d%d%d", &A, &X, &B, &Y);
	int max = (A > B ? A : B) + 2;        //二维数组的行列数,防止越界多申请一个
	int** arr = init(max);                //初始化杨辉三角数组
	for (int i = 0; i <= X; i++) {
		for (int j = 0; j <= Y; j++) {
			if (i * A + j * B == K) {      //若i首第一种歌和j首第二种歌长度加起来为K,则满足要求
				ans = getmod((ans + getmod(arr[X][i] * arr[Y][j])));    //防止数过大进行取模
			}
		}
	}
	printf("%lld\n", ans);
}

int main()
{
	test();
	return 0;
}

 

 

 

 

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值