做的第一道数位dp的题目,看出来明显的数位dp特征但是不会做。还是约定一个解题步骤,以设计思路、数据结构、算法分析、细节、备注的顺序解决。设计思路就是看出数位dp之后百度一下?至少知道怎么设置状态之后后面的细节才自己想。
数据结构:
- dp[i][j]表示数字位数为i位,最高位为j的合法数字的数量。
- 可以用pre[a]表示[0,a]的合法数字的总和,前缀和统计数量。
算法分析:
- 要转移到dp[i][j],最朴素的算法即是从dp[i-1][k](|j-k|<=2)添加一位j到最前面。每一步dp[i][j]转移为10,则dp[i]每推进一次就是100,最多推进10次,总花费是1000。
- 大问题来了,怎么统计前缀和的数量呢?不会做,又去看别人的代码。发现别人先把待统计数a分解成数组b[i],b[i]表示待统计数从右往左第i位是b[i],得到b[i]的长度len。那么所有dp[i][j](i<len)都会满足对应数比a小(毕竟少了一位)。然后dp[len][j](j<b[len])当然也是比a小(开头小)。那么开头一样的怎么统计呢?我们先固定最高位len,那么剩下的数字就和dp[len-1][j](j<b[len-1]&&|j-b[len]<=2|)对应起来,直到dp[len-1][len-1]我们就可以再固定len-1。这一次统计需要注意要保持和前一位差2。最后因为我们的前缀和包括a本身,就不需要特判,所以不要设计成[0,a)。复杂度100len,非常小。
细节:
- 为什么要这样设计数据结构呢?dp[i]应该是数位DP的套路,而题目中只涉及连续两位,那每次只保证最高位合法就可以了。使用前缀和则是因为前缀和统计起来很方便,注意前缀和设计的时候最好包含最后一位。
- 怎么确定算法的复杂度?随便乱搞一下就发现不会超时了。
- 怎么确定pre[a]和dp[i][j]之间的关系呢?见上文的复杂分析。
- 最后要求的是[A,B]之间的数目,那么按我们的语义就是pre[B]-pre[A-1],那么先预处理出dp数组,使用一个函数一次求出一个pre[a]即可。
代码:
//占坑
备注:
- 暂无