每日一练 - Python 非负整数求和的另一种方法

本文介绍了一种不使用内置大数库或直接转换为整数的方法来计算两个大数字符串的和。通过逐位相加并处理进位,确保了算法的准确性。

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

如果数字太大,不能将字符串转换为整数类型的时候,怎么运算数字呢?

原题

给定两个字符串形式的非负整数 num1num2 ,计算它们的和。

注意:

  • num1num2 的长度都小于 5100.

  • num1num2 都只包含数字 0-9. - num1num2 都不包含任何前导零。

  • 你不能使用任何内建 BigInteger 库, 也不能直接将输入的字符串转换为整数形式。

代码实现

程序从num1, num2的最后一位数开始,依次计算num1, num2同一数位的数字之,如果和大于10,则进位

要点:

  • add=s//10需要放在s=a1+a2+add之后,这样下一次计算才能获得前一次计算的进位。
  • 计算完最后一个num1, num2的数位后,如果有进位,计算结果还需要加上进位。
def solution(nums):
    num1,num2=nums
    if len(num1)<len(num2):
        num1,num2=num2,num1 # 交换num1, num2变量
    num2=num2.zfill(len(num1)) # num2 的开头用0填充,使num2的长度等于num1的长度
    result=""
    add=0
    for i in range(-1,-len(num2)-1,-1):
        a1,a2=int(num1[i]),int(num2[i])
        s = a1 + a2 + add
        #print(a1,a2,s)
        result = str(s%10) + result
        add=s//10 # 下一位的进位

    if add>0:
        result=str(add)+result # 计算结果加上最后一个进位

    return result

写完代码后,还需要进行测试。这里使用random模块,随机产生两个整数,进行计算。

from random import randint
for i in range(1000):
    num1,num2=str(randint(0,10000)),str(randint(0,10000))
    result=solution([num1,num2])
    if result != str(int(num1)+int(num2)):
         raise Exception("计算错误: %s %s %s"%(num1,num2,result))
    else:
        print("计算正确: %s %s %s"%(num1,num2,result))

部分运行结果:

计算正确: 3910 3129 7039
计算正确: 1018 4859 5877
计算正确: 3055 7940 10995
计算正确: 1018 1904 2922
计算正确: 4657 3795 8452
计算正确: 393 6770 7163
计算正确: 2512 2379 4891
计算正确: 5701 1903 7604

注:题目来自优快云的“每日一练”习题功能。
有兴趣的读者,可以对程序进行相应的改编,如计算减法、乘法、除法等。

欲获得更多每日一练的参考答案,请关注作者的“每日一练”专栏​​​​​​​!

<think>好的,我现在要解决这个台阶问题。问题的大意是说,有N级的台阶,每次可以迈1到K级,问到达第N级有多少种不同的方式,结果要对100003取模。我需要仔细理解这个问题,然后找出正确的解决方法。 首先,我应该考虑这个问题的递推关系。比如,当我要到达第n级台阶的时候,我之前可能是在n-1级、n-2级,直到n-K级的地方,然后一步迈上来。所以,总的方案数应该是前面这K个可能的台阶的方案数之。这听起来像是一个动态规划的问题。 举个例子,比如样例输入是52。那么,到达第5级的方法等于到达第4级第3级的方法。因为每次最多迈2步,所以从4级迈1步,或者从3级迈2步。然后,每个前面的级数也是同样的方式计算。 那动态规划的状态转移方程应该是这样的:dp[n] = dp[n-1] + dp[n-2] + ... + dp[n-K],这里n-K要大于等于0。如果n-K小于0的话,比如当n=1且K=2的时候,那么就只能取前面存在的项。例如,当n=2,K=2的时候,dp[2] = dp[1] + dp[0]。这里dp[0]应该初始化为1,因为站在地面可以看作一种初始情况。 那初始化的话,dp[0] = 1。然后,dp[1] = dp[0](当K>=1的时候)。当n小于K的时候,那么前面的项可能不足K个,比如当n=3,K=5的话,这时候可以迈1到3步,所以前面的所有可能的dp值相加。 那如何高效地计算这个呢?如果对于每个n,都累加前K个元素,当K很大而n很大时,这样的时间复杂度会是O(NK),可能不够高效。但题目中的N的范围是多少呢?题目没有说明,假设N可能很大,比如1e5级别的话,这样的方法可能超时。那有没有更优化的方法? 这时候可以考虑前缀或者滑动窗口的方法。因为每次计算dp[n]的时候,其实是前K个数的。那如果我们维护一个当前的sum,当计算到下一个n的时候,sum = sum + dp[n-1] - dp[n-K-1]。但要注意边界情况。例如,当n <= K的时候,sum是前面所有项的,而当n超过K之后,每次需要减去超出范围的项。 比如,初始化sum为dp[0] =1。然后对于n=1到N: 当n <= K时,dp[n] = sum,然后sum += dp[n]。而当n>K时,每次计算dp[n] = sum,然后sum += dp[n] - dp[n-K](或者类似的)。或者可能需要调整sum的更新方式。 或者,一种方式是维护一个窗口,比如,当前的是前K项的累加。例如,在计算dp[n]的时候,sum等于dp[n-1] + dp[n-2] + ... + dp[n-K}。然后每次循环时,sum减去dp[n-K-1](如果有的话),然后加上新的dp[n]。这可能比较复杂。 或者,可以用前缀数组。例如,pre_sum[n] = dp[0] + dp[1] + ... + dp[n]。那么,dp[n] = pre_sum[n-1] - pre_sum[n-K-1]。因为当n-K-1 >=0的话,那么sum是从n-K到n-1的。如果n-K-1 <0,那么sum就是pre_sum[n-1]。例如,当n=5,K=2的时候,pre_sum[4] - pre_sum[3-2=3?或者可能需要重新计算下标。可能需要仔细处理。 举个例子,当n=5时,K=2。那么dp[5] = dp[4] + dp[3]。而pre_sum[4]是dp0到4的,pre_sum[3-1=2]是dp0到2的。那么pre_sum[4] - pre_sum[2] = dp3+dp4。所以,正确的计算应该是当n >= K时,pre_sum[n-1] - pre_sum[n-1 -K]。比如,对于K=2,n-1-K = n-3。如果n-3 >=0,则差就是pre_sum[n-1] - pre_sum[n-3]。否则,就是pre_sum[n-1]。这样,不管n的大小如何,都可以正确计算出前K项的。 这样,动态转移方程可以写成: dp[n] = pre_sum[n-1] - pre_sum[n-K-1] ,当n > K时。或者等价于,sum = 前缀到n-1,减去前缀到n-1-K(如果存在的话)。 所以,我们可以维护一个前缀数组,这样每个dp[n]可以通过前缀数组快速计算出来,从而将时间复杂度降低到O(N),空间复杂度O(N)。 现在,具体的步骤可能如下: 1. 初始化dp数组,其中dp[0] =1,因为起点是0级,初始有一种方式。 2. 计算前缀数组pre_sum,其中pre_sum[0] = dp[0]。pre_sum[i] = pre_sum[i-1] + dp[i] 3. 对于每个n >=1,到N: dp[n] = (pre_sum[n-1] - (pre_sum[n-K-1] if n-K-1 >=0 else 0)) % mod 然后pre_sum[n] = (pre_sum[n-1] + dp[n]) % mod 这样,每次计算dp[n]的时候,只需要O(1)的时间,从而整个算法的时间复杂度是O(N)。 那这样的话,处理样例输入5,2: 初始dp[0] =1,pre_sum[0]=1. n=1: dp[1] = pre_sum[0] - pre_sum[1-2-1= -2] →取0 →1-0=1 →dp[1]=1. pre_sum[1] =1+1=2. n=2: pre_sum[1] - pre_sum[2-2-1= -1] →2-0=2 →dp[2]=2. pre_sum[2]=2+2=4. n=3: pre_sum[2] - pre_sum[3-2-1=0] →4 - pre_sum[0]=4-1=3 →dp[3] =3. pre_sum[3] =4+3=7. n=4: pre_sum[3] - pre_sum[4-2-1=1] →7 - pre_sum[1] =7-2=5 →dp[4] =5. pre_sum[4] =7+5=12. n=5: pre_sum[4] - pre_sum[5-2-1=2] →12 - pre_sum[2] =12-4=8 →dp[5]=8. 所以结果正确,样例输出是8。 这样看来,这个方法是可行的。 那现在需要编写代码实现这个思路。需要注意几点: - 数组的大小可能需要到N,但Python中数组索引是从0开始的,所以要注意。比如,当N=5的时候,数组的索引是0到5,共6个元素? 比如,假设输入是N级台阶,比如5级,那么最终的答案是dp[5]。因为问题中问的是到达第N级的方式数。比如,台阶编号是0到N吗?或者可能题目中的台阶编号是1到N?需要仔细看题目描述。 问题描述中说:“有N级的台阶,你一开始在底部,每次可以向上迈最多K级台阶(最少1级),问到达第N级台阶有多少种不同方式。”这里的底部应该是0级,第N级是最高级。所以,初始位置是0级,目标是N级。因此,当计算到N的时候,需要dp[N]的值。 所以在代码中,数组的索引应该到N。例如,输入是5,那么数组的大小应该是N+1,即0到5,共6个元素。 因此,在代码中,初始化的数组长度应该是N+1,从0到N。 所以,代码的大体结构: 输入NK。然后mod是100003. 初始化dp数组,dp[0] =1. pre_sum数组,pre_sum[0] =1. 然后循环n从1到N: 计算当前n的dp值: 如果n-K-1 >=0: dp[n] = (pre_sum[n-1] - pre_sum[n-K-1]) % mod 否则: dp[n] = pre_sum[n-1] % mod 但是,pre_sum数组的索引是n,所以当n >=1时,pre_sum[n] = pre_sum[n-1] + dp[n] 但是,在Python中,pre_sum数组可以动态构建。或者,可以用一个变量来维护前缀。或者,因为pre_sum数组的每个元素是当前的前缀总,所以pre_sum[n] = pre_sum[n-1] + dp[n] 这样的话,pre_sum数组可以提前初始化为一个长度为N+1的数组,或者动态生成。 例如,pre_sum数组初始化为长度N+1,pre_sum[0] =1. 然后,对于n in 1..N: dp[n] = (pre_sum[n-1] - (pre_sum[n-K-1] if (n-K-1)>=0 else 0 )) % mod pre_sum[n] = (pre_sum[n-1] + dp[n]) % mod 但是,mod的取余必须在每次操作中进行,否则数值可能会非常大,导致溢出。 所以,在Python中,可以用这样的方式: mod = 100003 n, k = map(int, input().split()) dp = [0]*(n+1) pre_sum = [0]*(n+1) dp[0] =1 pre_sum[0] =1 for i in range(1, n+1): # 计算dp[i] left = i -k if left <=0: # 当i <=k的时候,前面所有的dp[0...i-1]的,即pre_sum[i-1] dp_i = pre_sum[i-1] else: # i >k的时候,pre_sum[i-1] - pre_sum[left-1] dp_i = (pre_sum[i-1] - pre_sum[left-1]) dp[i] = dp_i % mod pre_sum[i] = (pre_sum[i-1] + dp[i]) % mod print(dp[n] % mod) 这样是否正确呢? 例如,当i=5,k=2时: i=5, left=5-2=3. 因为3>0,所以计算pre_sum[4] - pre_sum[2]. pre_sum[4]是前面到4的前缀,即pre_sum[4] =12(如之前的例子)。 pre_sum[2]是4。12-4=8,正确。 是的。那这样,代码是正确的吗? 测试样例输入5 2,输出是8,应该是正确的。 再举一个例子,比如n=1,k=1。此时,dp[1] = pre_sum[0] =1 →正确。输出1. 一个测试案例,n=3,k=3。此时,每次最多可以迈3步。到达3级的方式有: 迈1+1+1,1+2,2+1,3 →共4种。那么根据代码: pre_sum数组的构建过程: i=0: dp[0]=1, pre_sum=1. i=1: left=1-3= -2 <=0 →pre_sum[0]=1 →dp[1]=1. pre_sum=1+1=2. i=2: left=2-3=-1 <=0 →pre_sum[1]=2 →dp[2]=2. pre_sum=2+2=4. i=3: left=3-3=0 →即 i>k吗?当k=3时,i=3,left=0。此时,是否i>k?因为当k=3时,left=0的条件是i>=3。此时,代码中的条件判断是left <=0的话,取pre_sum[i-1]。否则,取pre_sum[i-1] - pre_sum[left-1]. 例如,当i=3,k=3时,left=0,所以left-1 =-1。此时,判断left <=0 →是的。所以,计算dp[3] = pre_sum[2] =4。那是否与实际的情况不符? 因为当i=3时,可能的步数是从0级迈3步,或者从1级迈2步,或者从2级迈1步。所以dp[3] = dp[0] + dp[1] + dp[2} =1+1+2=4。这与代码计算的结果一致。pre_sum[2]是4,所以正确。所以代码是正确的。 那这时候输出dp[3] =4,正确。那没有问题。 那看来代码是正确的。 现在需要处理一些特殊情况,比如当K=1的时候。例如,n=5,k=1。此时,每次只能迈一步,所以只有一种方式。所以输出1。代码是否正确? 比如,代码中的循环: 对于每个i从1到5: i=1时,left=0 →dp[1]=pre_sum[0]=1. pre_sum=2. i=2,left=1 →k=1,所以i- k=2-1=1>0?此时,left=1,所以进入else分支。dp_i=pre_sum[1] - pre_sum[0] →2-1=1. pre_sum=3. i=3: left=3-1=2>0 →pre_sum[2] - pre_sum[1] →3-2=1. pre_sum=4. 依此类推,每个dp[i] =1. 所以dp[5] =1,正确。 一个例子,n=2,k=1 →输出1. 所以代码是正确的。 那综上,这个动态规划的方法是正确的,并且通过维护前缀数组,将时间复杂度降到了O(N),空间复杂度O(N)。 现在需要编写Python代码,并且处理输入的nk。注意,输入的n可能等于0吗?比如,当n=0时,是否有台阶?根据问题描述,输入的nk是两个正整数,所以输入中的n>=1,k>=1。所以不需要处理n=0的情况。 外,当k大于n时,比如n=3,k=5的时候,此时每次最多可以迈5级,所以每次可以迈1到3级。这时,动态转移方程会自动处理,因为当i-K<=0时,sum的前面的所有项都会被计算进去。 所以代码可以正确处理这种情况。 现在编写代码: 读取输入nk,注意输入的格式是两个正整数。可能输入的格式如: 5 2 →正确。 然后初始化dp数组pre_sum数组。其中,dp的长度是n+1。pre_sum同理。 需要注意,Python中的列表索引是否正确。比如,i从1到n: 比如,代码: n, k = map(int, input().split()) mod = 100003 if n ==0: print(0) else: dp = [0]*(n+1) pre_sum = [0]*(n+1) dp[0] =1 pre_sum[0] =1 for i in range(1, n+1): left = i -k if left <=0: dp_i = pre_sum[i-1] else: dp_i = pre_sum[i-1] - pre_sum[left-1] dp[i] = dp_i % mod pre_sum[i] = (pre_sum[i-1] + dp[i]) % mod print(dp[n] % mod) 这样应该可以解决问题。 但需要注意的是,当计算dp_i的时候,可能会出现负数的情况,因为减法后的结果可能为负数,所以取模的时候需要处理。例如,如果pre_sum[i-1] - pre_sum[left-1]是负数的话,模运算后的结果可能不正确。但因为在模运算中,应该先加mod再取模,以确保结果非负。例如: dp_i = (pre_sum[i-1] - pre_sum[left-1]) % mod 这在Python中是可行的,因为Python的负数取模会自动处理,但为了确保正确性,应该这样处理。 例如,假设pre_sum[i-1] =5,pre_sum[left-1] =10,mod=100003。此时,5-10 =-5,取模后的结果是100003-5=99998,这是正确的。所以Python的取模处理是正确的。 综上,代码是正确的。</think>为了解决这个台阶问题,我们需要计算到达第N级台阶的不同方式数,每次可以迈1到K级台阶。我们可以使用动态规划前缀数组来高效地解决这个问题。 ### 方法思路 1. **动态规划**:我们使用一个数组`dp`来存储到达每一级台阶的方式数。`dp[i]`表示到达第i级台阶的方式数。 2. **递推关系**:到达第i级台阶的方式数等于到达前i-1, i-2, ..., i-K级台阶的方式数之。即: \[ dp[i] = dp[i-1] + dp[i-2] + ... + dp[i-K] \] 3. **前缀优化**:为了高效计算上述,我们使用前缀数组`pre_sum`,其中`pre_sum[i]`表示前i级台阶方式数的前缀。这样可以将每次求和的时间复杂度从O(K)优化到O(1)。 ### 解决代码 ```python n, k = map(int, input().split()) mod = 100003 dp = [0] * (n + 1) pre_sum = [0] * (n + 1) dp[0] = 1 pre_sum[0] = 1 for i in range(1, n + 1): left = i - k if left <= 0: dp_i = pre_sum[i-1] else: dp_i =
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值