P1192台阶问题 题目描述 有N级的台阶,你一开始在底部,每次可以向上迈最多K级台阶(最少11级),问到达第N级台阶有多少种不同方式。

本文针对P1192台阶问题,介绍了使用递推法求解的优化思路,并给出了具体实现代码。通过引入前缀和的概念,将原始复杂度降低到更优级别。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

大家好!
第一次写代码,手抖----
下面写P1192台阶问题的解析即代码
这题关系式很明显,几乎是斐波那契数列
所以可以用递推来做
f(n) = f(n-1) + f(n-2) + f(n-3) + … + f( max(0,n-k) )
max保证了递推式对于前k项也成立,因为f不能作用一个负数
但是直接这么递推复杂度为O(nk),只能得到40分,后面的全部tle掉
于是需要用前缀和的思想进行优化,即pf(n) = f(n) + f(n-1) + f(n-2) + … + f(0)
然后就有f(n) = pf(n-1) - pf( max(0,n-k) - 1 ),定义pf(-1) = 0
但是要注意每次计算完pf和f都要膜一下
但是膜完之后可能两个pf相减是个负数,这时就要加上100003
不懂dalao们是如何裸的递推还不TLE的。。。反正裸的递推我是TLE了。
当然也可以用矩阵加速递推,由于太麻烦,这里就不在讨论
#include
#include
#include
#include
#include
using namespace std;
typedef long long ll;
const int p = 100003;
ll rst[100010];
ll pf[100010];
int n,k;
ll getpf( int l , int r )
{
if( l == 0 ) return pf[r];
else
{
ll ret = pf[r] - pf[l-1];
if( ret < 0 ) ret += p;
return ret;
}
}
int main()
{
cin >> n >> k;
rst[0] = pf[0] = 1;
for( int i = 1 ; i <= n ; ++i )
{
int j = max(0,i-k);
rst[i] = getpf(j,i-1);
pf[i] = pf[i-1] + rst[i];
pf[i] %= p;
}
cout << rst[n] << endl;
return 0;
}

<think>嗯,用户的问题是关于计算到第N台阶不同方式数量,每次可以1到K台阶。我需要先理解这个问题,然后根据提供的引用资料来找到解决方法。首先,用户提到的每次可以走1到K,这可能涉及到动态规划或者组合数学的方法。根据引用[1]和[2],动态规划的方法是将问题分解为子问题,比如到第k台阶的方法数是前min(m, k-1)的方法数之和。这里用户的问题中的K应该对应这里的m。所以动态规划的状态转移方程可能是dp[k]= dp[k-1]+ dp[k-2]+ ...+ dp[k-K],当然需要考虑k是否大于等于K的情况。如果k小于K,那么可能只需要加到dp[0]的位置,并且当k<=K时,可能还要加1,比如直接步跨上去的情况。不过引用[5]中的代码显示,当k<=i时,循环j从1到k,所以当i >=j时,temp[i]累加temp[i-j]。这可能意味着当i>=j的情况下,每次从i-j的位置跳j步到i。这样的话,初始条件应该是temp[0] =1,因为到第0台阶方式(起点)。然后temp[1]=1,因为只能从01步到1。当i>=2时,根据不同的j值累加前面的结果。引用[3]中的例子显示当N=3,K=2时,有3种方法,这符合dp[3] =dp[2]+ dp[1],即2+1=3。同样的,当N=4,K=2时,dp[4] =dp[3]+ dp[2] =3+2=5,这也符合例子中的结果。这说明动态规划的方法是正确的。另外,引用[4]提到了使用组合数的方法,这可能是在某些特定情况下,比如当允许每次最多K步时,总方法数可能涉及组合数的计算。例如,当每次可以走1到K步,那么总步数可能有不同的组合方式。例如,假设总共有m步,其中每步的步数之和为N,且每步步数不超过K。这时候可能需要计算不同的排列组合。不过这种情况可能比较复杂,尤其是当K较大时,组合数的方法可能不太容易直接应用。不过根据用户的问题,他们可能需要的是动态规划的解决方案,因为引用[5]中的代码就是使用动态规划来解决的。所以应该先给出动态规划的思路,然后可能再提到组合数的方法,如果有适用的情况的话。接下来,需要确定动态规划的初始条件和递推关系。根据引用[5],初始条件是temp[0]=1,因为到第0方式只有种,即不动。对于i >=1的情况,temp[i]等于所有j从1到K的情况下,如果i >=j,那么temp[i]+= temp[i-j]。并且每步都要取模100003。这应该就是递推公式。所以动态规划的步骤应该是:1.初始化个数组dp,其中dp[0] =1,dp[1]=1(当N>=1时)。 2.对于每个i从2到N:dp[i] =sum(dp[i-j] forj in1..Kif i-j>=0) 3.最后返回dp[N] %100003不过需要验证是否正确。例如,当N=3,K=2时,dp[3] =dp[2]+ dp[1] =2+1=3,正确。当N=4,K=2时,dp[4]=dp[3]+dp[2]=3+2=5,正确。当K=3时,比如N=3,那么dp[3]= dp[2] +dp[1]+ dp[0} =1+1+1=3?或者可能有不同的初始条件?这里可能需要注意,当i<=K时,是否可以步到位。例如,当i=3,K=3,那么步跳3也是种方法,所以是否应该在动态规划中自动包括这种情况。根据递推公式,当计算dp[3]时,j从1到3,i-j分别是2,1,0,所以sum是dp[2]+dp[1]+dp[0},如果初始时dp[0}=1,那么当K=3时,dp[3}=dp[2}+dp[1}+dp[0}=2+1+1=4?但根据引用[3]中的例子,当N=3,K=2时,结果是3,而不是4。这说明可能当K=3时,允许步跳3,所以dp[3}应该等于之前的sum加上1?或者初始条件是否需要调整?这里可能存在矛盾。例如,当K=3,N=3时,是否允许直接跳3?根据递推公式,当计算dp[3}时,j=3,i-j=0,所以dp[0}=1,所以dp[3}=1(j=3的情况)加上其他j=1和j=2的情况。例如,当K=3,dp[3}=dp[2}+dp[1}+dp[0}=2+1+1=4,其中dp[0}=1对应直接跳3的情况。所以在这种情况下,动态规划的递推公式是正确的,不需要额外加1,因为当j=3时,i-j=0,而dp[0}=1已经考虑了这步。所以初始条件正确的情况下,递推公式会自动处理所有可能的跳跃方式。因此,动态规划的解法是正确的。接下来需要编写代码,如引用[5]中的C++代码,转换为Python的话,应该类似:def count_ways(n,k):mod=100003dp =[0] *(n+1)dp[0]=1fori inrange(1,n+1):forj inrange(1,k+1):ifi >=j:dp[i]+= dp[i-j]dp[i] %= modreturn dp[n]不过需要测试这个代码是否正确。例如,当n=3,k=2时,结果应该是3。计算过程:dp[0]=1i=1:j=1:i>=j→ dp[1] +=dp[0}→ dp[1}=1i=2: j=1: dp[2} +=dp[1}=1j=2:dp[2}+= dp[0}=1→ dp[2}=2i=3: j=1: dp[3} +=dp[2}=2j=2:dp[3}+= dp[1}=1→ dp[3}=3→正确。 当n=3,k=3时:dp[3}= dp[2} +dp[1}+ dp[0} =2 +1 +1=4 →正确,因为可以步跳3,所以方法数为4种:[1,1,1],[1,2], [2,1],[3}.所以代码是正确的。另外,引用[4]中的组合数方法可能适用于当允许最多K步,但每次可以走任意步数的情况下,但可能更复杂。例如,当总步数m,每步的步数之和为N,且每个步数<=K。这种情况下,可能可以用生成函数或者其他组合数学的方法,但可能不如动态规划直接。因此,用户的问题应该用动态规划来解决,代码如上述的Python实现。同时,需要注意取模的问题,因为结果可能很大,需要取模100003,如引用[5]中的做法。 最后,用户可能需要了解时间复杂度和优化方法。例如,动态规划的时间复杂度是O(N*K),当K较大时,这可能会比较慢。此时可以采用优化的方法,比如使用前缀和来减少计算sum的时间。例如,当计算dp[i}时,sum的范围是dp[i-1}到 dp[i-K}(如果i-K>=0),否则到dp[0}。可以利用滑动窗口或者前缀和数组来将时间复杂度降低到O(N)。例如,维护个当前的和,每次减去超出范围的dp[i-K-1},并加上新的dp[i-1},从而在O(1)时间内得到sum。这样时间复杂度变为O(N),适用于较大的K值。 例如,优化后的代码: defcount_ways_optimized(n,k):mod=100003dp =[0]*(n+1)dp[0] =1window_sum =1#sum ofdp[0..0]fori inrange(1,n+1):dp[i]= window_sum %modifi >=k:window_sum =(window_sum -dp[i- k] +mod)% modwindow_sum= (window_sum+ dp[i])% modreturn dp[n]这里需要验证是否正确。例如,当i=1,window_sum初始是1(dp[0}),所以dp[1}=1。然后window_sum减去dp[1 -k}(当k=2,i=1>=k?1>=2不成立,所以不减去。然后window_sum+= dp[1}=1→ window_sum=2。i=2时,dp[2}=window_sum=2。如果k=2,i=2>=k→是,所以window_sum减去dp[0}=1→变成2-1=1,然后加上dp[2}=2→window_sum=3。i=3时,dp[3}=3(window_sum=3)。然后i=3>=k=2→是的,所以window_sum减去dp[1}=1 →3-1=2,然后加上dp[3}=3 →window_sum=5。这似乎有问题,可能我的优化方法需要重新考虑。或者,正确的优化方法可能需要维护个窗口大小为k的滑动和。例如,dp[i}= sum(dp[i-1}, dp[i-2},..., dp[i-k}]),其中i >=1。所以,当i <=k时,窗口是i-1到0。例如,当i=1,窗口是0到0;当i=2,窗口是1到0(如果k>=2)等。因此,维护个窗口的和,当i超过k时,每次减去dp[i -k -1},并加上dp[i}。或者,可能需要重新考虑如何计算前缀和。例如,可以维护个前缀和数组,其中prefix[i}= sum(dp[0}..dp[i}]),然后sum(dp[i -k}..dp[i -1}])等于prefix[i -1}- prefix[i -k -1}。这样,当i <=k时,sum是prefix[i -1},因为i-k-1可能为负数,此时prefix[-1}视为0。这样,dp[i}= (prefix[i-1} -(prefix[i -k -1}if i-k-1>=0else0))%mod。然后prefix[i}= (prefix[i-1} +dp[i}) )% mod。这样可以将时间复杂度降到O(n)。例如,代码实现: defcount_ways_optimized(n,k):mod=100003dp =[0]*(n+1)prefix= [0]*(n+1)dp[0]=1prefix[0] =1for iin range(1, n+1):left =max(0,i -k)right= i-1if left==0:sum_val= prefix[right]else:sum_val= (prefix[right] -prefix[left-1]) %moddp[i]= sum_val %modprefix[i]= (prefix[i-1] +dp[i]) %modreturndp[n] %mod测试这个代码是否正确:当n=3,k=2:i=1: left=max(0,1-2)=0sum_val =prefix[0}=1 →dp[1}=1prefix[1}=1+1=2i=2:left =max(0,2-2)=0sum_val= prefix[1}=2→ dp[2}=2prefix[2}=2+2=4i=3: left= max(0,3-2)=1sum_val= prefix[2} -prefix[0}=4-1=3→ dp[3}=3正确。当k=3,n=3时:i=1:left=0 →sum_val=prefix[0}=1→ dp[1}=1prefix[1}=2i=2: left= max(0,2-3)=0→ sum_val=prefix[1}=2 →dp[2}=2 →prefix=2+2=4i=3: left= max(0,3-3)=0→ sum_val=prefix[2}=4 →dp[3}=4 →正确。所以这种方法有效,时间复杂度为O(n),因为每个i的处理都是O(1)。这对于较大的k和n来说更高效。因此,用户的问题可以通过动态规划解决,并且可以通过优化将时间复杂度从O(nk)降低到O(n)。这是需要向用户说明的。总结步骤: 1.确定动态规划状态定义:dp[i]表示到第i台阶的方法数。2.初始化dp[0]=1,因为起点是第0。 3.递推公式:dp[i] =sum(dp[i-j] forj in1..kif i>=j)4.优化递推公式,使用前缀和或滑动窗口来减少计算时间。5.返回dp[n]% mod。 用户可能还需要了解如何处理大数问题,比如取模运算,以及时间复杂度的分析。此外,可能还会问及边界条件,比如n=0的情况(应该返回1?但根据问题描述,可能n>=1)。 根据引用[3]中的例子,当n=1时,方法数是1,符合dp[1}=1。所以代码处理正确。现在,根据用户的问题,我需要将上述思考整理成个清晰的回答,包括动态规划的基本思路、优化方法,以及可能的组合数学方法(如果有必要的话)。同时,需要参考提供的引用资料,特别是引用[5]中的代码示例,并转换为Python代码。此外,需要生成相关问题,如用户要求的。</think>### 台阶问题解法分析 要计算到第$N$台阶不同方式数(每次1至$K$台阶),可采用动态规划方法。设$dp[i]$表示到第$i$台阶的方法数,其核心思想是**将问题分解为子问题的累加**。 #### 1. 基本动态规划解法 - **状态转移方程**: $$dp[i] = \sum_{j=1}^{\min(K,i)} dp[i-j]$$ 表示从第$i-j$台阶步跨$j$$i$的所有可能路径之和。 - **初始条件**: $$dp[0] = 1 \quad (\text{起点唯性})$$ - **代码实现**: ```python def count_ways(n, k): mod = 100003 dp = [0] * (n + 1) dp[0] = 1 for i in range(1, n + 1): for j in range(1, k + 1): if i >= j: dp[i] += dp[i - j] dp[i] %= mod return dp[n] ``` #### 2. 时间复杂度优化 当$K$较大时,上述算法的时间复杂度为$O(NK)$。可通过**前缀和优化**将复杂度降至$O(N)$: - **优化思路**:维护个窗口大小为$K$的前缀和数组,避免重复计算累加。 - **优化代码**: ```python def count_ways_optimized(n, k): mod = 100003 dp = [0] * (n + 1) prefix = [0] * (n + 1) dp[0], prefix[0] = 1, 1 for i in range(1, n + 1): left = max(0, i - k) sum_val = (prefix[i-1] - (prefix[left-1] if left > 0 else 0)) % mod dp[i] = sum_val prefix[i] = (prefix[i-1] + dp[i]) % mod return dp[n] ``` #### 3. 组合数学方法(特殊场景) 当允许**每次最多$K$**且$N$较小时,可通过组合数计算总步数。例如,总步数为$m$时,需满足: $$\sum_{s=1}^m x_s = N \quad (1 \leq x_s \leq K)$$ 但此方法需要处理重复排列,复杂度较高[^4]。 --- ### 示例分析 - **当$N=3, K=2$**: $dp[3] = dp[2] + dp[1] = 2 + 1 = 3$,对应方式:`[1,1,1], [1,2], [2,1]`[^3]。 - **当$N=4, K=3$**: $dp[4] = dp[3] + dp[2] + dp[1} = 4 + 2 + 1 = 7$,包含步跨3的情况。 --- ### 关键点总结 1. **动态规划核心**:拆解子问题,避免重复计算[^5]。 2. **边界条件**:$dp[0] = 1$保证递推起点正确。 3. **取模操作**:防止整数溢出,需每步计算后取模[^5]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值