首页 > 其他 > 详细

动态规划—简单数位dp

时间:2020-02-07 19:03:52      阅读:68      评论:0      收藏:0      [点我收藏+]
 

HDU2089

杭州人称那些傻乎乎粘嗒嗒的人为62(音:laoer)。
杭州交通管理局经常会扩充一些的士车牌照,新近出来一个好消息,以后上牌照,不再含有不吉利的数字了,这样一来,就可以消除个别的士司机和乘客的心理障碍,更安全地服务大众。
不吉利的数字为所有含有4或62的号码。例如:
62315 73418 88914
都属于不吉利号码。但是,61152虽然含有6和2,但不是62连号,所以不属于不吉利数字之列。
你的任务是,对于每次给出的一个牌照区间号,推断出交管局今次又要实际上给多少辆新的士车上牌照了。

Input

输入的都是整数对n、m(0<n≤m<1000000),如果遇到都是0的整数对,则输入结束。
Output

对于每个整数对,输出一个不含有不吉利数字的统计个数,该数值占一行位置。
Sample Input

1 100
0 0

Sample Output

80

 

数位dp之前听说了一下,今天终于学到了,原来就是根据数字的位数(千位、十位、百位等)做dp,然后就是求某个区间上符合某条件的数的个数,一般就用数位dp来解决,毕竟暴力几乎T。

对于上面这道题,求的是区间上不含有4和62的数字的个数,直接求出0到这两个数直接符合条件的数字,然后做差就行了。

第一种dp方法:

用dp[i][j]表示第i位上为j-1到j的所有数字中符合条件的数字的个数。比如dp[2][1]就是0~10中符合条件数字的个数,dp[3][2]就是100~200之间符合条件的数字的个数。

用状态转移方程来表示就是:

       0, j=4;
dp[i][j]= ∑ dp[i-1][k],(0<=k<=9), j!=6;
       ∑ dp[i-1][k],(0<=k<=9&&k!=2), j=6;

然后在把给出的数字XnXn-1……X1(这是一个数字,xi表示第i位上的数)分成Xn*10^(n-1)+X(n-1)*10^(n-2)+……+X1,把每一位上符合条件的数字求出来就好了。

代码:

 1 #include<iostream>
 2 using namespace std;
 3 int n,m,dp[8][10];
 4 void init()
 5 {
 6   dp[0][0]=1;
 7   for(int i=1;i<=9;i++)
 8   {
 9     for(int j=0;j<=9;j++)
10     {
11       if(j==4)dp[i][j]=0;
12       else {
13         for(int k=0;k<=9;k++)
14         {
15           if(j==6&&k==2)continue;
16           dp[i][j]+=dp[i-1][k];
17         }
18       }
19     }
20   }
21 }
22 int find(int x)
23 {
24   int ans=0,cnt=0,p[10];
25   while(x)
26   {
27     p[++cnt]=x%10;
28     x/=10;
29   }
30   p[cnt+1]=0;
31   for(int i=cnt;i>=1;i--)
32   {
33     for(int j=0;j<p[i];j++)
34     {
35       if(j==4||(j==2&&p[i+1]==6))continue;
36       ans+=dp[i][j];
37     }
38     if(p[i]==4||(p[i]==2&&p[i+1]==6))break;
39   }
40   return ans;
41 }
42 int main()
43 {
44   init();
45   while(cin>>n>>m&&(n||m))cout<<find(m+1)-find(n)<<endl;
46 }

 

不明白find函数的建议用笔模拟一遍。还有就是结果是f(m+1)-f(n)是为什么?这是因为f(m)求的是[0,m)区间上的个数,并没有把m这个数算进去,f(m+1)-f(n)得到的就是[n,m]上的答案啦。

 

第二种dp方法:


这个方法用到了dfs和记忆化搜索,还好在这之前学了记忆化搜索和dfs,不然不可能搞懂这个鬼东西的。

我是看这大佬

先贴上代码:

 

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 int a[8],f[10][2];
 4 int dfs(int d,bool is6,bool limit)
 5 {
 6   if(d==0)return 1;
 7   if(!limit&&f[d][is6]!=-1)return f[d][is6];
 8   int cur=0,end=(limit?a[d]:9);
 9   for(int i=0;i<=end;i++)
10   {
11     if((is6&&i==2)||i==4)continue;
12     cur+=dfs(d-1,i==6,limit&&i==end);
13   }
14   if(!limit)f[d][is6]=cur;
15   return cur;
16 }
17 int solve(int x)
18 {
19   memset(a,0,sizeof(a));
20   int cnt=0;
21   if(!x)a[++cnt]=0;
22   while(x){a[++cnt]=x%10;x/=10;}
23   return dfs(cnt,0,1);
24 }
25 int main()
26 {
27   memset(f,-1,sizeof(f));
28   int n,m;
29   while(cin>>n>>m&&(n||m))
30   {
31     cout<<solve(m)-solve(n-1)<<endl;
32   }
33   return 0;
34 }

 

 

 

1,先看 dfs(int d,bool is6,bool limit) 这里的d是指数字的第几位,is6是判断这个位置上的数字是不是6,limit用来判断这个位置上的数字有没有达到最大

2,f[d][is6]是用来记忆化搜索的,记忆第d位是6和不是6的数的个数,不用每次都重复去算,可以加快dfs的速度

3,第8行是判断上界用的,如果这一位数已经到了上界,那么就从0最大放到a[d],否则一直放到9

4,因为solve(m)求的是[0,m]区间,所以答案应该是solve(m)-solve(n-1)





动态规划—简单数位dp

原文:https://www.cnblogs.com/hailin545/p/12271544.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!