动态规划讲解

动态规划深度训练经典问题

问题一: 给你一个长度为 50 的数字串,问你有多少个子序列构成的数字可以被 3 整除,答案对 1e9+7 取模。

输入描述: 输入一个字符串,由数字构成,长度小于等于 50

输出描述: 输出一个整数

不难发现就是要判断是否对某一位数选还是不选 的问题,应该是用 DP 解决。我们从三种情况来考虑这个问题。

一维DP:

(1)一维 DP 很多同学觉得这个用一维是不可能的,但是如果大家学的通透,完全可以实现,且不难理解。 其实无论数字多大,对 3 取余只有三个结果 0,1,2。我们用 dp[0]、dp[1]、dp[2]分别存储 除 3 得到的余数为 0,1,2 的数量,能不能每增加一个数就更新一遍数组。 下面的式子是否能看懂:

一.余数为 0:

dp[0]=dp[0]+dp[0]+1; 
dp[1]=dp[1]+dp[1]; 
dp[2]=dp[2]+dp[2];

二.余数为 1:

dp[0]=dp[0]+dp[2];

//如果新的余数为 1,原来余数为 2 的就变成了能被 3 整除了, 就要把 dp[2]的加到原来 dp[0]里面,能理解了吧? 

dp[1]=dp[1]+dp[0]+1; 
dp[2]=dp[2]+dp[1];

三.余数为 2:

dp[0]=dp[0]+dp[1]; 
dp[1]=dp[1]+dp[2]; 
dp[2]=dp[2]+dp[0]+1;

二维 DP

如果是二维 dp[i][j],i 很容易理解,应该是选前 i 个数。

仍然以 132 举例,当取第 1 个数字 1 的时候,其实余数为 1,这个 1 需要保存,因为后面有用

所以 j 应该是对 3 取模后的余数, 那么 0 <= j <= 2。

剩下的就是递推公式了。

(1)如果第 i 个数不取,那么

 dp[i][j]=dp[i-1][j];

(2)如果取第 i 个数,首先是包含 dp[i-1][j]个,132 肯定包含 13 的所有个数。

再就要看看 这位数对 3 求模和前面的余数加起来的情况了。

若求得余数为 1,那么当前这个余数,与前 i-1 位余数为 1 的情况,可以组成 2,与前面余数为 0 的情况又可以组成 1.怎么表达呢?

dp[i][j] = (dp[i - 1][j] + dp[i - 1][(j + 3 - 当前余数) % 3]) % Mod;

三维 DP

三维肯定是 dp[i][j][k]:表示第 i 位取/不取时余数为 k 时的子序列个数,j 取 0 或 1,0 <= k<= 2。

(1)不取 i 位,那应该是前面取或不取的和

dp[i][0][k] = dp[i-1][0][k] + dp[i-1][1][k];

(2)取第 i 位,这位的余数情况,要根据自身位数和前面余数的关系确定。

dp[i][1][k] = dp[i-1][0][p] + dp[i-1][1][p] ;

p 应该满足:

(p * 10 % 3 + a[i])% 3 = k;

暴力 p 的范围[0,2],满足条件时就转移 。

你们明白了吗?

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值