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

騰訊校招筆試題之小Q的歌單

題目

小Q的歌單
熱度指數:1624 時間限制:1秒 空間限制:32768K

小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
輸出
9

解答

一開始想用暴力求解來做,提交之後發現執行時間超過限制,只通過了60%的test cases。後來想到了動態規劃,這應該是一道很典型的動態規劃題目,具體思路如下。

思路
1. 用陣列dp來儲存結果,dp[i][j]表示用前j首歌表示長度為i的歌單的方法數,i=1…K,j=1,…,na+nb。na是第1種歌的數量,nb是第2種歌的數量
2. lens[j]表示第j首歌的長度,如果i>=lens[j],那麼由前j首歌組成長度為i的歌單的方法數可分為兩部分,第一部分是dp[i][j-1],即由前j-1首歌組成長度為i的歌單的方法數,第二部分是dp[i-lens[j]][j-1],即由j-1首歌組成長度為i-lens[j]的歌單的方法數,因為第j首歌已經佔據了lens[j]的長度。
3. 如果

i<lens[j],那麼dp[i][j]等於dp[i][j1]

程式碼

#include<iostream>
#include<stdlib.h>
#include <math.h>
using namespace std;

int main()
{
    int i, j, K, la, lb, na, nb,result;
    cin >> K;
    cin >> la >> na >> lb >> nb;
    if
(K <= 0 || la <= 0 || na <= 0 || lb <= 0 || nb <= 0) { cout << "error!"; return 0; } int* lens = new int[na+nb+1]; for (i = 1; i <= na; i++) { lens[i] = la; } for (i = 1; i <= nb; i++) { lens[i+na] = lb; } int** dp= new int*[K+1]; for (i = 0; i < K + 1; i++) { dp[i] = new int[na + nb + 1]; } for (j = 0; j < na + nb + 1; j++) { dp[0][j] = 1; } for (i = 1; i <= K ; i++) { if (lens[1] != i) { dp[i][1] = 0; } else { dp[i][1] = 1; } } for (i = 1; i <= K; i++) { for (j = 2; j < na + nb + 1; j++) { if (i >= lens[j]) { dp[i][j] = (dp[i][j-1]+dp[i-lens[j]][j-1]) % 1000000007; //dp[i][j] = (dp[i][j - 1] + dp[i - lens[j]][j - 1]); } else { dp[i][j] = dp[i][j - 1] % 1000000007; } } } cout << dp[K][na+nb] << endl; system("pause"); return 0; }