链接:https://www.nowcoder.com/questionTerminal/f3ab6fe72af34b71a2fd1d83304cbbb3
来源:牛客网
[编程题]小Q的歌单
- 热度指数:9845 时间限制: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
思路描述
将所有的歌都放入一个数组length中,为了计算方便不会越界,应该在数组开头补一个0,故例子中3首长度为2,3首长度为3的数组就该建为length=[0,2,2,2,3,3,3].
再设置一个二维数组dp[][],并初始化为(目标长度+1)*(len(length)+1)。其中dp[ i ][ j ]代表歌曲列表中的前 j 个元素所包含的能够加起来达到长度 i 的组合数(每一个组合所包含歌曲的个数在0 ~ j之间)。
当i = 0时,dp[ i ][ j ]也就意味着前j首歌所包含的能够加起来达到长度0 的组合数,这个很显然是1,也就是所有的歌都不添加到和中,因此需要在一开始就把dp[ 0 ][ j ]置为1。
当i > 0, j = 0时,dp[ i ][ j ]的意思就是前0首歌所包含的能够加起来达到长度i 的组合数,这个显然是0,因为0首歌并不能达到 i>0的长度。
此时dp为(空白的地方才是dp)
0 | 2 | 2 | 2 | 3 | 3 | 3 | |
---|---|---|---|---|---|---|---|
0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 |
1 | |||||||
2 | |||||||
3 | |||||||
4 | |||||||
5 |
若是 j >0:
如果 length[j] > i,那么第j首歌一定无法添加到任意组合中去,因为组合出的长度和已经大于 i 了,因此第 j 首歌的加入并不能使得前 j 个元素所包含的满足条件的组合数变多,因此 dp[ i ][ j ] = dp[ i ][ j - 1] ;
如果 length[j] = i , 那么第j首歌的加入能够使得前j个元素所包含的满足条件的组合数比前 j - 1首歌所包含的组合数多 1 ,多出来的1就是直接一个第 j首歌的长度刚好够了 i 。
如果 length[j] < i, 那么第 j 首歌的加入也许能够使得前j 个元素所包含的满足条件的组合数增加,这个取决于 dp[ i - length[ j ] ][ j - 1 ],如果该值不为0,那么第j首歌加入之后这些组合加上第j首歌的长度刚好达到了i (就是 i - length[ j ] + length[ j ]) ,此时就要把两种情况的组合数相加: dp[ i ][ j ] = dp[ i ][ j - 1] + dp[ i - length[ j ] ][ j - 1];
dp最后的结果为
0 | 2 | 2 | 2 | 3 | 3 | 3 | |
---|---|---|---|---|---|---|---|
0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 |
1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
2 | 0 | 1 | 2 | 3 | 3 | 3 | 3 |
3 | 0 | 0 | 0 | 0 | 1 | 2 | 3 |
4 | 0 | 0 | 1 | 3 | 3 | 3 | 3 |
5 | 0 | 0 | 0 | 0 | 3 | 6 | 9 |
import sys
if __name__ == "__main__":
data=[]
while True:
line = sys.stdin.readline().strip()
if not line:
break
tmp = list(map(int, line.split(" ")))
data.append(tmp)
n=data[0][0]
l=data[1]
length=[0]
for i in range(l[1]):
length.append(l[0])
for i in range(l[-1]):
length.append(l[-2])
def gedan(n,length):
dp=[[0 for i in range(len(length))]for i in range(n+1)]
for i in range(5):
dp[0][i]=1
for j in range(1,n+1):
dp[j][0]=0
for i in range(1,n+1):
for j in range(1,len(length)):
if length[j] > i:
dp[i][j] = dp[i][j-1]
elif length[j] == i:
dp[i][j]=dp[i][j-1]+1
else:
dp[i][j] = dp[i][j-1]+dp[i-length[j]][j-1]
return dp[n][len(length)-1]%1000000007
print(gedan(n,length))