腾讯----小Q的歌单
騰訊----小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 取模的結果。輸入例子1:
5 2 3 3 3輸出例子1:
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,即剩下的歌單長度可以在B中組合出來) && (( K - i* A)/ B <= Y :不能超過白球的數目),將滿足條件的結果相加起來就是最后的結果。
- 而當 滿足條件后從各自的盒子里拿球就有不同的拿法,是很典型的排列組合問題,對于這道題我們可以建一個二維數組來存這些組合數,行標代表排列組合公式的下標,列標代表排列組合公式的上標
- 可以直接看代碼:
方法二:動規
這道題和背包問題類似。
1. 第一步要明確兩點,「狀態」和「選擇」
先說狀態,如何才能描述一個問題局面?只要給定幾個可選的歌曲和一個長度為K的限制,就形成了一個背包問題,對不對?所以狀態有兩個,就是「歌曲組成的總長度K」和「可選擇的歌」。
再說選擇,也很容易想到啊,對于每首歌,你能選擇什么?選擇就是「裝進背包:算上這首歌的時長」或者「不裝進背包:不算這首歌的時長」嘛。
明白了狀態和選擇,動態規劃問題基本上就解決了,只要往這個框架套就完事兒了:
for 狀態1 in 狀態1的所有取值:for 狀態2 in 狀態2的所有取值:for ...dp[狀態1][狀態2][...] = 擇優(選擇1,選擇2...)2. 第二步要明確dp數組的定義
dp數組是什么?其實就是描述問題局面的一個數組。換句話說,我們剛才明確問題有什么「狀態」,現在需要用dp數組把狀態表示出來。
首先看看剛才找到的「狀態」有兩個,也就是說我們需要一個二維dp數組,一維表示可選擇的歌曲,一維表示歌曲總時長。
dp[i][w]的定義如下:對于前i首歌曲,當前歌曲總時長為w,這種情況下組合歌曲的方法有dp[i][w]種。
比如說,如果 dp[3][5] = 6,其含義為:對于給定的一系列歌曲中,若只對前 3 個歌曲進行選擇,當歌曲總時長K為 5 時,最多的組合方法有6種。
根據這個定義,我們想求的最終答案就是dp[N][W]。base case 就是dp[0][…] = dp[…][0] = 0,因為沒有歌曲或者歌曲總時長為0的時候,能構選擇的組合方法就是 0種,不過我們認為當沒有歌曲和歌曲總時長為0都滿足的時候認為算一種方法,即dp[0][0] == 1。
細化上面的框架:
int dp[N+1][W+1] dp[0][..] = 0 dp[..][0] = 0for i in [1..N]:for w in [1..W]把歌曲 i 裝進背包,不把歌曲 i 裝進背包) return dp[N][W]3. 第三步,根據「選擇」,思考狀態轉移的邏輯
簡單說就是,上面偽碼中「把歌曲i裝進背包」和「不把歌曲i裝進背包」怎么用代碼體現出來呢?
這一步要結合對dp數組的定義和我們的算法邏輯來分析:
先重申一下剛才我們的dp數組的定義:
dp[i][w]表示:對于前i首歌曲,當前總時長為w時,這種情況下可以組合的方法是dp[i][w]種。
如果你沒有把這第i個歌曲裝入背包,那么很顯然,最大價值dp[i][w]應該等于dp[i?1][w]。你沒有選擇一首歌曲,總時長也就不會變,那就繼承之前的結果。
如果你把這第i首歌曲裝入了背包,那么dp[i][w]應該等于dp[i?1][w?wt[i?1]] + val[i?1]。
首先,由于i是從 1 開始的,所以對val和wt的取值是i-1。
而dp[i?1][w?wt[i?1]]也很好理解:你如果想裝第i首歌曲,你怎么計算這時候的組合方法?換句話說,在裝第i首歌曲的前提下,總時長的最大組合方法是多少?
顯然,你應該尋求剩余時長w?wt[i?1]限制下組合的方法,加上第i首歌曲的時長val[i?1],這就是裝第i首歌曲的前提下,總時長的組合方法。
4.到這里基本上一背包問題的方式解釋了一遍本題,但是這道題是沒有每個物品 的價值的,所以需要根據單首歌曲的時長構造這樣一組價值信息
for(int i=1;i<=x;i++)len[i]=a; for(int i=x+1;i<=length;i++)len[i]=b;5.每首歌曲的‘價值’有了,那么在什么情況下選擇加入歌曲,什么情況下不能加入歌曲??
lens[j]表示第j首歌的長度,如果當前剩余的歌曲時長 >= lens[j],那么由前j首歌組成長度為i的歌單的方法數可分為兩部分,第一部分是dp[j - 1][i],即由前j-1首歌組成長度為i的歌單的方法數(不選取這首歌),第二部分是dp[j-1][i-lens[j]](選取這首歌)。
6. 如果當前剩余的總時長 < lens[j],那么dp[i][j]等于dp[i][j?1]。
代碼中dp[i][j]和上面描述有點不一樣(手誤):dp[i][j]代表用j首歌組成總時長i的方法有多少種,剛好寫反了,不過道理是一樣的。
#include <iostream> #include <algorithm> #include<string.h> #include<map> #include<iterator> #include<math.h> using namespace std; const int mod = 1000000007;int k;//總時長 int a,x,b,y;//a代表a歌曲的長度,x代表a歌曲的數量... int dp[1010][210];//dp數組 int len[210];//存儲每首歌的時長 int main() {cin>>k;cin>>a>>x>>b>>y;int length = x + y;memset(dp,0,sizeof(dp));memset(len,0,sizeof(len));//base casedp[0][0] = 1;//根據每首歌的時長構造‘價值’數組for(int i = 1;i <= x;i++)len[i] = a;for(int i = x + 1;i <= length;i++)len[i] = b;//循環for(int i = 0;i <= k;i++){ for(int j = 1;j <= length;j++){//如果當前剩余歌曲的總時長大于單首歌曲的時長//那么對于這首歌有可選和不選兩種選擇//把組合的結果相加,都算滿足情況if(i >= len[j]){dp[i][j] = (dp[i][j - 1] + dp[i - len[j]][j - 1]) % mod;}//如果當前剩余歌曲的總時長小于單個個的時長//代表不能選取這首歌,如果選取總時長就肯定會變長//不滿足情況else{dp[i][j] = dp[i][j - 1] % mod;}}}cout<<dp[k][length]<<endl;return 0; } 超強干貨來襲 云風專訪:近40年碼齡,通宵達旦的技術人生總結
以上是生活随笔為你收集整理的腾讯----小Q的歌单的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 腾讯----贪吃的小Q
- 下一篇: 腾讯---画家小Q