这题我们用动态规划做!
首先我们来找规律:
对于一个递增的数列,如123456,我们插入一个数。这个数大于数列中所有的数,这里插入7。如果不插在两端(1, 6)的数两侧,则增加了两个拐点,如1273456;插在(1, 6)的内测,有两种情况,如1723456和1234576;插在两端,有两种情况,7123456和1234567。
向一个数列的拐点两侧插入数字时,如果这个拐点比两边的数大,则拐点数不增加,如132变成1432或1342;如果这个拐点比两边的数小,则拐点数加2,如312变成3412或3142.
然后我们可以把插入数分为不增加拐点,增加1个拐点和增加两个拐点,共三种情况。
让dp[i][j]表示长度为i的列,拐点数为j时的所有排列的种数。
令大于两端数的拐点为第一类拐点,小于为第二类拐点。
情况一:不增加拐点,在dp[i-1][j]中插入数字i,我们发现第一类拐点的总数+第二类拐点的总数为j,而对于每种排列,都能让它的第一类拐点变成第二类,第二类变成第一类,如1423>>-1-4-2-3>>4132 及取负数在加上最大数再加一。因此可以证明所有情况的第一类拐点与第二类拐点的总和相同,及对于一种情况,选取第一类拐点,总有另一种情况,选择对应位置的第二类拐点(可以理解为把所有情况两两合并,要选的第一类拐点数总和就为j)。因此有2*0.5*dp[i-1][j]*j种情况,(2代表拐点两侧,0.5*dp[i-1][j]*j为所有第一类拐点的总数,)。然后还可以在两端外侧插入数字i,有dp[i-1][j]种情况。加起来就是(j+1)*dp[i-1][j]。
情况二:加一个拐点,只能在两端数的外侧和内侧加,也是两两合并的思想,有2*dp[i-1][j-1]种情况。
情况三:加两个拐点,对应dp[i-1][j-2]。同样的,总有两种情况的第一类拐点总数加起来为j-2,每个第一类拐点两端不能加故有2*(j-2)个位置不能加。两端数的外侧不能加,因此还有i-2个空位,两种情况就是2*(i-j)个空位。所以增加了0.5*dp[i-1][i-2]*(2*i-2*j)种情况。
得出状态方程:
dp[i][j] = (j+1)*dp[i-1][j] + 2*dp[i-1][j-1] if i > j > 1: dp[i][j] += dp[i-1][j-2]*(i-j)
# 考虑j<2时没有可以增加两个的情况
下面是代码
n, k = map(int, input().split())
# 创建dp
dp = [[0]*k for _ in range(n)]
for i in range(1, n):
# 这里长度其实为i+1,从长度为二的数列开始算
# 没拐点情况就两种,单增与单减
dp[i][0] = 2
for j in range(1, k):
dp[i][j] = (j+1)*dp[i-1][j] + 2*dp[i-1][j-1]
# 特殊情况,显然i总是要大于j的,拐点数不可能比数列还长,而j-1要大于等于0
if i > j > 1:
dp[i][j] += dp[i-1][j-2]*(i-j)
# 题目要求模上123456
dp[i][j] %= 123456
print(dp[n-1][k-1])
蓝桥杯练习系统100分,看不懂可以私信问我。