剑指Offer43:1~n整数中1出现的次数(数位DP)

 解题思路:数位DP。dp数组为dp[pos][sum]表示当前位以下还有pos个可变位并且当前位以及它的最高位出现了sum个1的dp值。因为数的取值为2^31所以,数组开dp[10][10]就够了。

数位DP入门博客:

https://www.luogu.com.cn/blog/virus2017/shuweidp#

https://blog.csdn.net/qq_25957237/article/details/102877820

 1 class Solution:
 2     def dfs(self,pos,one_sum,limit):
 3         #如果剩余可变位为0,范围当前数所有的one_sum,即当前状态的之前位有多少个1了
 4         if pos==0:
 5             return one_sum
 6         #如果没有受限并且dp值被记录过了,直接返回状态值
 7         if not limit and self.dp[pos][one_sum]!=-1:
 8             return self.dp[pos][one_sum]
 9         up = 9
10         if limit:
11             up = self.nums[pos-1]
12         ans =0
13         for i in range(up+1):
14             #状态转移方程:如果当前位是1,那么next_one_sum +=1
15             if i==1:
16                 nxt_one_sum = one_sum + 1
17             else:
18                 nxt_one_sum = one_sum
19             #搜索下一位,下一个状态的limit的由当前limit控制并且当前的值真的到了当前位的限制值.
20             #以365为例,搜索0??,1??,2??的limit都为false,只有当搜索3??的时候才继续为真.
21             ans+= self.dfs(pos-1,nxt_one_sum, limit and i==self.nums[pos-1])
22 
23         if not limit:#如果没有限制,说明下次再访问dp[pos][one_sum]的状态是一样的,记忆化
24             self.dp[pos][one_sum] = ans
25         return ans
26 
27     def solve(self,n):
28         self.nums = []
29         while n:
30             self.nums.append(n%10)
31             n//=10
32         #自高位向低位进行数位dp,因为从高位开始dp,因此limit为真
33         return self.dfs(len(self.nums),0,True)
34     def countDigitOne(self, n):
35         # dp[pos][sum]表示当前位以下还有pos个可变位并且当前位以及它的最高位出现了sum个1的dp值
36         #比如对于356这个数,0??的dp值应该与2??dp值一样为dp[2][0],但是与3??的dp值不同,因为3是最高位,受到limit的制约
37         #它不能保存dp值,除非再添加一个limit的维度(dp[10][10][2]),1??的dp值为dp[2][1]
38         self.dp = [[-1]*10 for _ in range(10)]
39         cnt = self.solve(n)
40         #print(self.dp[2][0])
41         #print(self.dp[2][1])
42         return cnt
43 
44 
45 
46 c = Solution()
47 #n = 302
48 z = c.countDigitOne(365)
49 print(z)

 

posted @ 2021-03-31 13:27  ISGuXing  阅读(152)  评论(0编辑  收藏  举报