[编程题] 小Q的歌单
【题目描述】 小Q有X首长度为A的不同的歌和Y首长度为B的不同的歌,现在小Q想用这些歌组成一个总长度正好为K的歌单,每首歌最多只能在歌单中出现一次,在不考虑歌单内歌曲的先后顺序的情况下,请问有多少种组成歌单的方法
输入描述:
每个输入包含一个测试用例。
每个测试用例的第一行包含一个整数,表示歌单的总长度K(1<=K<=1000)。
接下来的一行包含四个正整数,分别表示歌的第一种长度A(A<=10)和数量X(X<=100)以及歌的第二种长度B(B<=10)和数量Y(Y<=100)。保证A不等于B。
输出描述:
输出一个整数,表示组成歌单的方法取模。因为答案可能会很大,输出对1000000007取模的结果。
输入例子1:
5
2 3 3 3
输出例子1:
9
解题思路:
1.令
a*A+b*B=K
(
0
≤
a
≤
X
,
0
≤
b
≤
Y
)
\fbox{a*A+b*B=K} (0≤a≤X,0≤b≤Y)
a*A+b*B=K(0≤a≤X,0≤b≤Y),获得集合
(
a
,
b
)
(a,b)
(a,b)。
2.对每种集合都
C
(
X
,
a
)
∗
C
(
Y
,
b
)
C(X, a) * C(Y,b)
C(X,a)∗C(Y,b)种组合
3.最后进行累加求和,得到一共多少种不同的组合类型。
难点:
- 求
C
(
n
,
k
)
C(n,k)
C(n,k),n、k的范围是1~100
因为 C ( k n ) = C ( k − 1 n − 1 ) + C ( k − 1 n ) C\begin{pmatrix} k \\ n \\ \end{pmatrix} = C\begin{pmatrix} k - 1 \\ n - 1 \\ \end{pmatrix} + C\begin{pmatrix} k - 1 \\ n \\ \end{pmatrix} C(kn)=C(k−1n−1)+C(k−1n),可以用动态规划,使用二维数组来表示组合数,求组合数矩阵。arr[i][j]
表示从i中取j的种数,即 C ( i , j ) C(i,j) C(i,j) - 需要int、long数值范围,和数据溢出导致不能AC。 对于C++需要使用long long,long依赖于系统是32位还是64位的版本
C/C++ 版
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
using namespace std;
#define mod 1000000007
// C++中必须使用long long 类型,才不会溢出
long long arr[101][101];
//TODO:组合数初始化
void initialArr()
{
arr[0][0] = 1;
for (int i = 1; i <= 100; i++)
{
arr[i][0] = 1;
for (int j = 1; j <= i; j++)
{
arr[i][j] = (arr[i - 1][j - 1] + arr[i - 1][j]) % mod; // 组合数C(n, k) = C(n-1, k-1) + C(n-1, k)
}
}
}
// 计算结果
long long calculateApproach(int targetLen, int firstLen, int firstNum, int secondLen, int secondNum)
{
long long res = 0;
for (int i = 0; i <= firstNum && firstLen * i <= targetLen; i++) // 当targetLen能由i首firstLen歌曲和 (targetLen - firstLen * i) / secondLen首secondLen歌曲组成时
{
if ((targetLen - firstLen * i) % secondLen == 0 && (targetLen - firstLen * i) / secondLen <= secondNum)
{
// 注意取模
long long temp = (long)((arr[firstNum][i] * arr[secondNum][(targetLen - firstLen * i) / secondLen]) % mod);
res = (res + temp) % mod;
//res = (res + (arr[firstNum][i] * arr[secondNum][(targetLen - firstLen * i) / secondLen]) % mod) % mod;
}
}
return res;
}
int main()
{
int targetLen = 5, firstLen = 2, firstNum = 3, secondLen = 3, secondNum = 3;
// scanf和printf 比cin和cout的效率更高
scanf("%d", &targetLen);
scanf("%d%d%d%d", &firstLen, &firstNum, &secondLen, &secondNum);
// cin >> targetLen;
// cin >> firstLen >> firstNum >> secondLen >> secondNum;
initialArr();
long long res = calculateApproach(targetLen, firstLen, firstNum, secondLen, secondNum);
printf("%d\n", res);
system("pause");
return 0;
}
java版
import java.io.IOException;
import java.util.Scanner;
/**
* Time: 2019-01-06 Author: snowy
* 腾讯2018秋招笔试题1: 小Q的歌单
* 【题目描述】 小Q有X首长度为A的不同的歌和Y首长度为B的不同的歌,现在小Q想用这些歌组成一个总长度正好为K的歌单,每首歌最多只能在歌单中出现一次,在不考虑歌单内歌曲的先后顺序的情况下,请问有多少种组成歌单的方法
* 输入描述:
* 每个输入包含一个测试用例
* 每个测试用例的第一行包含一个整数,表示歌单的总长度K(1<=K<=1000)
* 接下来的一行包含四个正整数,分别表示歌的第一种长度A(A<=10)和数量X(X<=100)以及歌的第二种长度B(B<=10)和数量Y(Y<=100)。保证A不等于B。
* 输出描述:
* 输出一个整数,表示组成歌单的方法取模。因为答案可能会很大,输出对1000000007取模的结果.
* */
public class SongList {
long mod = 1000000007;
public static long[][] arr = new long[101][101];
// 用来计算组合数的
public void initialArr(long[][] arr) {
arr[0][0] = 1;
for (int i = 1; i < 101; i ++) {
arr[i][0] = 1;
for (int j = 1; j <= i; j ++) {
arr[i][j] = (arr[i - 1][j - 1] + arr[i - 1][j]) % mod; // 计算组合数: C(n, k) = C(n-1, k-1) + C(n-1, k)
}
}
}
public int caculateApproach(int targentLen, int firstLen, int firstNum, int secondeLen, int secondNum) {
int res = 0;
for (int i = 0; i < firstNum; i ++) {
if (firstLen * i <= targentLen && (targentLen - firstLen * i) % secondeLen == 0 && (targentLen - firstLen * i) / secondeLen <= secondNum) {
// 注意取模
//res += (int)((long)(arr[firstNum][i] * arr[secondNum][(targentLen - firstLen * i) / secondeLen]) % mod);
long temp = (long)(arr[firstNum][i] * arr[secondNum][(targentLen - firstLen * i) / secondeLen]);
res = (int)((res + temp)%mod);
}
}
return res;
}
public static void main(String[] args) {
SongList sl = new SongList();
System.out.println("pleace input the test example:");
Scanner input = new Scanner(System.in);
int targetLen = input.nextInt();
int firstLen = input.nextInt();
int firstNum = input.nextInt();
int secondLen = input.nextInt();
int secondNum = input.nextInt();
sl.initialArr(arr);
int ans = sl.caculateApproach(targetLen, firstLen, firstNum, secondLen, secondNum);
System.out.println(ans);
}
}