题目描述
头脑并不发达的warden最近在思考一个问题,她的闪烁技能是可以升级的,k级的闪烁技能最多可以向前移动k个监狱,一共有n个监狱要视察,她从入口进去,一路上有n个监狱,而且不会往回走,当然她并不用每个监狱都视察,但是她最后一定要到第n个监狱里去,因为监狱的出口在那里,但是她并不一定要到第1个监狱。
守望者warden现在想知道,她在拥有k级闪烁技能时视察n个监狱一共有多少种方案?
输入数据
第一行是闪烁技能的等级 k (1≤k≤10)
第二行是监狱的个数 n (1≤n≤231−1)
输出数据
由于方案个数会很多,所以输出它 mod 7777777后的结果就行了
样例输入
2
4
样例输出
5
这道题的话其实如果只是当做动态规划的其实是挺简单的,因为这个的子问题其实很好分,这样一来思路其实就很明确了,这里题目说的是到最后的n监狱的方案,其实就是n-k到n-1这几个监狱的方案数之和(这个应该就不用多说了吧)
这样一来,我们就有了第一个版本的代码提交:
k=int(input())
n=int(input())
a=[0]
for i in range(1,n+1):
add=0
flag=0
for j in range(1,k+1):
if i-j<=0 and flag==0:
add+=1
flag=1
else:
add+=a[i-j]
a.append(add)
print(a[n]%7777777)
这个代码本身没什么问题,考虑了前面k个监狱的直接跳的次数,整个列表从左到右很好算,但是问题其实是很明显的:
第一,只有最后一个模除的话其实前面的计算压力还是很大
第二,按照最大的数据规模来看,难道要开一个九位数的数组吗,这明显不合适。
所以很明显,这个题这样在OJ上是过不了的。。
接下来就需要进行一下思路的转变了,子问题公式不变,所以新的方案不算难想,就是用一个k长的列表,先存前k个监狱的方案数,之后每一次都是更新其中的一个元素同时放弃监狱号最小的,最后得到结果。这个方案对于空间的占用明显会小不少,但是在这里我们会发现一下,那就是如果列表每一次改变的值的下标不一定的话其实是比较麻烦的,而且我们意识到,如果用矩阵乘法实现数值更新的话,不仅可以调整位置,更新数据,而且可以利用矩阵结合律+快速幂来简化计算,这样我们就得到了最后的代码:
k=int(input())
n=int(input())
mod=7777777
a=[]
sum=0
for i in range(k):
add=0
add=sum+1
a.append(add)
sum+=add
# print(a)
if k>=n:
print(a[n-1])
else:
b=[[0 for i in range(k)]for i in range(k)]
for i in range(k):
b[i][k-1]=1
if i+1<k:
b[i+1][i]=1
# print(a)
def mulMatrix(x,y,k):
ans = [[0 for i in range(k)]for j in range(k)]
for i in range(k):
for j in range(k):
for l in range(k):
ans[i][j] = (ans[i][j] + (x[i][l] * y[l][j])%mod)%mod
return ans
def quickMatrix(m,n,k):
E = [[0 for i in range(k)]for j in range(k)]
for i in range(k):
E[i][i] = 1
while(n):
if n % 2 == 1:
E = mulMatrix(E,m,k)
m = mulMatrix(m,m,k)
n=int(n/2)
return E
def mymul(x,y,k):
mid=[0 for i in range(k)]
for i in range(k):
for j in range(k):
mid[i]=(mid[i]+(x[j]*y[j][i])%mod)%mod
return mid
# print(quickMatrix(b,n,k))
# print(quickMatrix(b,n-k,k))
print(mymul(a,quickMatrix(b,n-k,k),k)[k-1])
思路对了之后,这里需要注意的点就少了很多,主要是矩阵的乘法这里,因为矩阵乘法中的一维乘二维和二维乘二维还是有一些不一样的,这里需要写两个函数。(主要是兼容的那个要进行数值类型变化而我又刚好不会)。最后成功AC。
这次这道OJ题目属实是顶级折磨,我做这道题直接碰到了基本全部类型的错误(除了TLE),实在是太过经典了所以我一定要把这个摆出来(捂脸)