1. 程式人生 > >騰訊筆試題--------小Q的歌單

騰訊筆試題--------小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取模的結果

輸入示例:
    5
    2 3 3 3

輸出示例:
    9

分析:這道題比較好的處理方法也最容易理解的是用組合數來求解,說到組合數就很容易想到這道題和我們高中做的從兩個盒子裡取小球的排列組合題。

此題可以轉換為這樣一道數學題,有兩個盒子,一個盒子裡裝有3個紅球,一個盒子裡裝有3個白球,紅球代表2分,白球代表3分,則從兩個盒子中任意拿球使其分數等於9的拿法有多少種。

這樣就會想如果拿了0個紅球,白球有多少種拿法,如果拿了1個、2個、3個紅球,白球各有多少種拿法。

再者,將球的數量和球的分數換成未知的量:即有兩個盒子,一個盒子裡裝有X個紅球,一個盒子裡裝有Y個白球,紅球代表A分,白球代表B分,則從兩個盒子中任意拿球使其分數等於K的拿法有多少種。很顯然就和麵試題一樣了,可以想到假設拿了 i 個紅球( i  <= X),需要滿足條件( i * A <= K : 分數不能超過K)&&(( K - i* A)% B == 0  :確保分數相加等於K) &&  (( K - i* A)/  B  <= Y  :不能超過白球的數目),將滿足條件的結果相加起來就是最後的結果。

而當滿足條件後從各自的盒子裡拿球就有不同的拿法,是很典型的排列組合問題,對於這道題我們可以建一個二維陣列來存這些組合數,行標代表排列組合公式的下標,列標代表排列組合公式的上標,具體的存法和楊輝三角有些類似,可以直接看程式碼:

#include <stdio.h>

long long c[105][105];
const int mod = 1000000007;

void init()   //計算組合數
{
	c[0][0] = 1;
	for(int i = 1;i <= 100;i++)
	{
		c[i][0] = 1;
		for(int j = 1;j <= 100;j++)
		c[i][j] = (c[i - 1][j - 1] + c[i - 1][j]) % mod;
	}
}

int main()
{
    int k;                //歌單總長度
	int a;                //長度為a的歌 
	int b;                //長度為b的歌 
	int x;                //長度為a的歌有x首
	int y;                //長度為b的歌有y首
	long long ans = 0;    //組成歌單的種類數
	init();
	scanf("%d",&k);
	scanf("%d%d%d%d",&a,&x,&b,&y);
	for(int i = 0;i <= x;i++)
	{
		if(i * a <= k && (k - i * a) % b == 0 && (k - i * a) / b <= y) //滿足題目的判斷條件
		{
			ans = (ans + (c[x][i] * c[y][(k - i * a) / b]) % mod) % mod;
		}
	}
	printf("%lld\n",ans);
    return 0;
}

執行結果如下所示: