题目描述
小明喜欢在火车旅行的时候用手机听音乐,他有N首歌在手机里,在整个火车途中,他可以听P首歌,所以他想产生一个播放表产生P首歌曲,这个播放表的规则是:
(1)每首歌都要至少被播放一次
(2)在两首一样的歌中间,至少有M首其他的歌
小明在想有多少种不同的播放表可以产生,那么给你N,M,P,你来算一下,输出结果取1000000007的余数
输入描述:
输入N,M,P
N范围在1到100
M范围在0到N
P范围在N到100
输出描述:
输出结果mod 1000000007的余数
1 import math 2 n,m,p = map(int,input().split()) 3 dp = [[0 for _ in range(p+1)] for _ in range(n+1)] 4 for i in range(m+1,n+1): 5 for j in range(i,p+1): 6 if j == i or i == m+1: 7 dp[i][j] = math.factorial(i) 8 else: 9 dp[i][j] = dp[i-1][j-1] * i + dp[i][j-1] * (i-m) 10 print(dp[n][p]%1000000007)
算法思路:动态规划。
本题目要计算的是,用n首不同的歌曲组成一个长度为p的歌曲列表,有多少种可能。
其中有两个条件:
1每首歌曲至少要播放一次,那必定p>=n,否则本条件无法满足,这个条件是隐含的,容易被忽略;
2相同的两首歌中间,至少间隔m首其他的歌曲。
举一个实际的例子来说明本题目的思路。假设n=4,m=2,p=5。
定义dp[n+1][p+1]二维数组,初始值为0。每一个元素表示由i首歌组成长度为j的列表,有多少可能。显然dp[n][p]即为所求。
i=0,表示有0首歌,其值都为0,表示不可能组成歌单。
i=1,表示有1首歌,要分两种情况讨论:
如果m=0,则值为1;如果m>=1,则值为0。本题m=2,因此值均为0。
i=2,表示有2首歌,
如果m=0,则2*2=4;如果m=1,则2*1=2;如果m>=2,则值为0。本题m=2,因此值为0。
由以上分析可发现,从第0行到第m行的值均为0,那么只需要从第m+1行开始计算即可。见代码第4行:range(m+1,p+1)
i=3,表示有3首歌,本题m=2,
j=3,第一个位置有3种选择,第二个位置有2种选择,第二个位置有1种选择。见代码第7行math.factorial()计算阶乘。
j=4,要计算的是3首歌生成一个长度为4的歌单,把此问题分成两部分考虑:
第一部分,2首歌生成长度为3的歌单,间隔2首。左上 * i
第二部分,3首歌生成长度为3的歌单,间隔2首。左 * (i - m)
结果为两部分之和。
但是为什么这样划分,我还没有想明白。只能先记录下来吧,也许以后能理解吧。