专题训练之数位DP

推荐以下一篇博客:https://blog.csdn.net/wust_zzwh/article/details/52100392

1.(HDOJ2089)http://acm.hdu.edu.cn/showproblem.php?pid=2089

分析:裸模板题

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 using namespace std;
 5 typedef long long ll;
 6 int a[20];
 7 int dp[20][2];
 8 int dfs(int pos,int pre,int sta,bool limit)
 9 {
10     if ( pos==-1 ) return 1;
11     if ( !limit && dp[pos][sta]!=-1 ) return dp[pos][sta];
12     int up=limit?a[pos]:9;
13     int tmp=0;
14     for ( int i=0;i<=up;i++ )
15     {
16         if ( pre==6 && i==2 ) continue;
17         if ( i==4 ) continue;
18         tmp+=dfs(pos-1,i,i==6,limit&&i==a[pos]);
19     }
20     if ( !limit ) dp[pos][sta]=tmp;
21     return tmp;
22 }
23
24 int solve(int x)
25 {
26     int pos=0;
27     while ( x )
28     {
29         a[pos++]=x%10;
30         x/=10;
31     }
32     return dfs(pos-1,-1,0,true);
33 }
34
35 int main()
36 {
37     int l,r;
38     while ( scanf("%d%d",&l,&r)!=EOF && (l+r) )
39     {
40         memset(dp,-1,sizeof(dp));
41         printf("%d\n",solve(r)-solve(l-1));
42     }
43     return 0;
44 }

HDOJ2089

2.(HDOJ3555)http://acm.hdu.edu.cn/showproblem.php?pid=3555

题意:求区间内不出现49的数的个数

分析:裸模板题

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 using namespace std;
 5 typedef long long ll;
 6 int a[20];
 7 ll dp[20][2];
 8 ll dfs(int pos,int pre,int sta,bool limit)
 9 {
10     if ( pos==-1 ) return 1;
11     if ( !limit && dp[pos][sta]!=-1 ) return dp[pos][sta];
12     int up=limit?a[pos]:9;
13     ll tmp=0;
14     for ( int i=0;i<=up;i++ )
15     {
16         if ( pre==4 && i==9 ) continue;
17         tmp+=dfs(pos-1,i,i==4,limit&&i==a[pos]);
18     }
19     if ( !limit ) dp[pos][sta]=tmp;
20     return tmp;
21 }
22
23 ll solve(ll x)
24 {
25     int pos=0;
26     while ( x )
27     {
28         a[pos++]=x%10;
29         x/=10;
30     }
31     return dfs(pos-1,-1,0,true);
32 }
33
34 int main()
35 {
36     ll l,r,T;
37     memset(dp,-1,sizeof(dp));
38     scanf("%lld",&T);
39     while ( T-- )
40     {
41         scanf("%lld",&r);
42         printf("%lld\n",r-solve(r)+1);
43     }
44     return 0;
45 }

HDOJ3555

3.(HDOJ4734)http://acm.hdu.edu.cn/showproblem.php?pid=4734

题意:题目给了个f(x)的定义:F(x) = An * 2n-1 + An-1 * 2n-2 + ... + A2 * 2 + A1 * 1,Ai是十进制数位,然后给出a,b求区间[0,b]内满足f(i)<=f(a)的i的个数。

分析:采用相减的思想,dp[i][j],第一维表示处于数字的第几位(即pos),第二维表示是枚举到当前pos位,后面位数最多能凑的权值和为j(起始值为f(a)),最后当j>=0是满足条件的数。具体解释见上面的博客

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 using namespace std;
 5 typedef long long ll;
 6 const int maxn=10010;
 7 int a[12];
 8 int dp[12][maxn];
 9 int sum;
10 int pow_[maxn];
11
12 int dfs(int pos,int sta,bool limit)
13 {
14     if ( pos==-1 ) return 1;
15     if ( !limit && dp[pos][sta]!=-1 ) return dp[pos][sta];
16     int up=limit?a[pos]:9;
17     int tmp=0;
18     for ( int i=0;i<=up;i++ )
19     {
20         int x=pow_[pos]*i;
21         if ( sta-x<0 ) continue;
22         tmp+=dfs(pos-1,sta-x,limit&&i==a[pos]);
23     }
24     if ( !limit ) dp[pos][sta]=tmp;
25     return tmp;
26 }
27
28 int solve(int x)
29 {
30     int pos=0;
31     while ( x )
32     {
33         a[pos++]=x%10;
34         x/=10;
35     }
36     return dfs(pos-1,sum,true);
37 }
38
39 int main()
40 {
41     int l,r,T,i,j,k,h,A,B,x,y,z,cnt;
42     pow_[0]=1;
43     for ( i=1;i<=8;i++ ) pow_[i]=pow_[i-1]*2;
44     scanf("%d",&T);
45     memset(dp,-1,sizeof(dp));
46     for ( h=1;h<=T;h++ )
47     {
48         scanf("%d%d",&A,&B);
49         sum=0;
50         cnt=0;
51         x=A;
52         while ( x )
53         {
54             y=x%10;
55             sum+=y*pow_[cnt++];
56             x/=10;
57         }
58         printf("Case #%d: %d\n",h,solve(B));
59     }
60     return 0;
61 }

HDOJ4734

4.(POJ3252)http://poj.org/problem?id=3252

题意:求一个范围内满足,二进制下0的位数>=1的位数的个数

分析:将原先的十进制一位转化为二进制一位.此题要在dfs中添加bool型的lead表示前导0,因为这题需要拿0的个数和1的个数比较,所以需要考虑前导0.同时dp数组的第二维记录的是0的个数-1的个数。因为可能为负,所以初始值不为0,而记一个较大的数(我记的是32,只要能使得过程不为负即可)

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 using namespace std;
 5 typedef long long ll;
 6 int a[50];
 7 ll dp[50][66];
 8 ll dfs(int pos,int sta,bool lead,bool limit)
 9 {
10     if ( pos==-1 ) return sta>=32;
11     if ( !limit && !lead && dp[pos][sta]!=-1 ) return dp[pos][sta];
12     int up=limit?a[pos]:1;
13     ll tmp=0;
14     for ( int i=0;i<=up;i++ )
15     {
16         if(lead && i==0) tmp+=dfs(pos-1,sta,lead,limit && i==a[pos]);
17         else tmp+=dfs(pos-1,sta+(i==0?1:-1),lead && i==0,limit && i==a[pos]);
18     }
19     if ( !limit&&!lead ) dp[pos][sta]=tmp;
20     return tmp;
21 }
22
23 ll solve(ll x)
24 {
25     int pos=0;
26     while ( x )
27     {
28         a[pos++]=x%2;
29         x/=2;
30     }
31     return dfs(pos-1,32,true,true);
32 }
33
34 int main()
35 {
36     ll l,r;
37     memset(dp,-1,sizeof(dp));
38     while ( scanf("%lld%lld",&l,&r)!=EOF )
39     {
40         printf("%lld\n",solve(r)-solve(l-1));
41     }
42     return 0;
43 }

POJ3252

5.(HDOJ5179)http://acm.hdu.edu.cn/showproblem.php?pid=5179

题意:给定一个数A,a数组从0开始对应着数A从左到右的每一尾,现在要求数A右边的数都不比左边的数大,同时要求在左边的数去模右边的数都为0

分析:dp数组的第二维记录前一位数是多少即可。转移时要转移到比前一位数小的数同时要被前一位数取模为0的数

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 using namespace std;
 5 typedef long long ll;
 6 int a[20];
 7 int dp[20][12];
 8 int dfs(int pos,int sta,bool lead,bool limit)
 9 {
10     if ( pos==-1 ) return 1;
11     if ( !limit && !lead && dp[pos][sta]!=-1 ) return dp[pos][sta];
12     int up=limit?a[pos]:9;
13     int tmp=0;
14     for ( int i=0;i<=up;i++ )
15     {
16         if ( !lead&&i==0 ) continue;
17         if ( lead )
18         {
19             tmp+=dfs(pos-1,i,lead && i==0,limit&&i==a[pos]);
20             continue;
21         }
22         if ( i>sta&&sta!=-1 ) break;
23         if ( sta%i!=0 && sta!=-1 ) continue;
24         tmp+=dfs(pos-1,i,lead && i==0,limit&&i==a[pos]);
25     }
26     if ( !limit&&!lead ) dp[pos][sta]=tmp;
27     return tmp;
28 }
29
30 int solve(int x)
31 {
32     int pos=0;
33     while ( x )
34     {
35         a[pos++]=x%10;
36         x/=10;
37     }
38     return dfs(pos-1,-1,true,true);
39 }
40
41 int main()
42 {
43     int l,r,T;
44     scanf("%d",&T);
45     memset(dp,-1,sizeof(dp));
46     while ( T-- )
47     {
48         scanf("%d%d",&l,&r);
49         printf("%d\n",solve(r)-solve(l-1));
50     }
51     return 0;
52 }

HDOJ5179

6.(HDOJ3652)http://acm.hdu.edu.cn/showproblem.php?pid=3652

题意:给定一个范围,求该范围内不含13同时不是13倍数的数的个数

分析:设置三维数组dp[i][j][k],第一维表示位置,第二维表示13的余数,

第三维有三个值,0代表此前还未出现过13同时前一位不为1,1代表此前还未出现过13同时前1位位1,2代表此前已经出现过了13

最后判断时,只有当第二维为0,第三维为2时才加入计数

注意第三维转移时到底是多少

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 using namespace std;
 5 typedef long long ll;
 6 int a[20];
 7 int dp[20][15][3];
 8 int dfs(int pos,int rem,int sta,bool limit)
 9 {
10     if ( pos==-1 ) return rem==0&&sta==2;
11     if ( !limit && dp[pos][rem][sta]!=-1 ) return dp[pos][rem][sta];
12     int up=limit?a[pos]:9;
13     int tmp=0;
14     for ( int i=0;i<=up;i++ )
15     {
16         int x=sta;
17         if ( sta==0 && i==1 ) x=1;
18         if ( sta==1 && i!=1 ) x=0;
19         if ( sta==1 && i==3 ) x=2;
20         tmp+=dfs(pos-1,(rem*10+i)%13,x,limit&&i==a[pos]);
21     }
22     if ( !limit ) dp[pos][rem][sta]=tmp;
23     return tmp;
24 }
25
26 int solve(int x)
27 {
28     int pos=0;
29     while ( x )
30     {
31         a[pos++]=x%10;
32         x/=10;
33     }
34     return dfs(pos-1,0,0,true);
35 }
36
37 int main()
38 {
39     int l,r;
40     memset(dp,-1,sizeof(dp));
41     while ( scanf("%d",&r)!=EOF )
42     {
43         printf("%d\n",solve(r));
44     }
45     return 0;
46 }

HDOJ3652

原文地址:https://www.cnblogs.com/HDUjackyan/p/9142156.html

时间: 2024-11-11 01:06:07

专题训练之数位DP的相关文章

「kuangbin带你飞」专题十五 数位DP

传送门 A.CodeForces - 55D Beautiful numbers 题意 一个正整数是 漂亮数 ,当且仅当它能够被自身的各非零数字整除.我们不必与之争辩,只需计算给定范围中有多少个漂亮数. 思路 因为问你的是一段区间内有多少数能整除他的所有非零数位 1-9,1,一定能被任何正整数整除,1-9的最小公倍数为2520 而1-2520中真正是1-9中的最小公倍数的只有48个 dp i j k:因为dp25,2520,[2520]开不下,所以我们要进行适当离散化 index[]数组标记1-

kuangbin带你飞 - 专题十五 - 数位dp

https://vjudge.net/contest/70324 A - Beautiful numbers 统计区间内的,被数位上各个为零数字整除的数的个数. 下面是暴力的数位dp写法,绝对会TLE的,因为这个要深入到每个数字的最后才能判断是否合法.因为(错误的状态设计导致完全变成暴力dfs搜索)记忆化的意义在询问不多的时候用处不大就去掉了.果然2400分的题不能这么暴力. #include<bits/stdc++.h> using namespace std; #define ll lon

2019年9月训练(壹)数位DP (HDU 2089)

开学之后完全没时间写博客.... HDU 2089 不要62(vjudge) 数位DP 思路: 题目给出区间[n,m] ,找出不含4或62的数的个数 用一个简单的差分:先求0~m+1的个数,再减去0~n的个数. 但问题依旧不简单,再次简化为求0~i位数中不含4或62的数的个数. i=1 //0~9中 i=2 //0~99中 i=3 //0~999中 ...... dp[i][0] //0~i位数中的吉利数 dp[i][1] //0~i位数中以2打头的吉利数 dp[i][2] //0~i位数中的非

专题训练之区间DP

例题:以下例题部分的内容来自https://blog.csdn.net/my_sunshine26/article/details/77141398 一.石子合并问题 1.(NYOJ737)http://acm.nyist.edu.cn/JudgeOnline/problem.php?pid=737 分析:我们dp[i][j]来表示合并第i堆到第j堆石子的最小代价.那么状态转移方程为dp[i][j]=min(dp[i][k]+dp[k+1][j]+w[i][j])  (s[i][j-1]<=k<

HDU 4507 吉哥系列故事――恨7不成妻(数位dp&amp;好魔性的一道好题)

题目链接:[kuangbin带你飞]专题十五 数位DP J - 吉哥系列故事――恨7不成妻 题意 Time Limit:500MS Memory Limit:32768KB 64bit IO Format:%I64d & %I64u Description 单身! 依然单身! 吉哥依然单身! DS级码农吉哥依然单身! 所以,他生平最恨情人节,不管是214还是77,他都讨厌! 吉哥观察了214和77这两个数,发现: 2+1+4=7 7+7=7*2 77=7*11 最终,他发现原来这一切归根到底都是

HDU 2089 不要62(数位dp&amp;记忆化搜索)

题目链接:[kuangbin带你飞]专题十五 数位DP C - 不要62 题意 杭州人称那些傻乎乎粘嗒嗒的人为62(音:laoer). 杭州交通管理局经常会扩充一些的士车牌照,新近出来一个好消息,以后上牌照,不再含有不吉利的数字了,这样一来,就可以消除个别的士司机和乘客的心理障碍,更安全地服务大众. 不吉利的数字为所有含有4或62的号码.例如: 62315 73418 88914 都属于不吉利号码.但是,61152虽然含有6和2,但不是62连号,所以不属于不吉利数字之列. 你的任务是,对于每次给

CodeForces 55D Beautiful numbers(数位dp&amp;&amp;离散化)

题目链接:[kuangbin带你飞]专题十五 数位DP A - Beautiful numbers 题意 ps:第一道数位dp,题真好,虽然是参考大牛方法悟过才a,但仍收获不少. 求一个区间内的Beautiful numbers有多少个.Beautiful numbers指:一个数能整除所有组成它的非0数字. 例如15可以被1和5整除,所以15是Beautiful numbers. 思路 Beautiful numbers指:一个数能整除所有组成它的非0数字. 等同于 一个数能整除 所有组成它的

POJ 3252 Round Numbers(数位dp&amp;amp;记忆化搜索)

题目链接:[kuangbin带你飞]专题十五 数位DP E - Round Numbers 题意 给定区间.求转化为二进制后当中0比1多或相等的数字的个数. 思路 将数字转化为二进制进行数位dp,由于一个二进制数的最高位必须为1.所以设置变量first记录前面位是否有1,若有1,则可随意放,否则,仅仅可放1. 同一时候.上面的推断决定了搜索时len的大小与二进制本身的长度不一定相等,所以需两个变量对1和0的个数进行记录. 用dp[a][b][c]保存长度a,b个0,c个1的数字个数.记忆化搜索.

HDU 3652 B-number(数位dp&amp;记忆化搜索)

题目链接:[kuangbin带你飞]专题十五 数位DP G - B-number 题意 求1-n的范围里含有13且能被13整除的数字的个数. 思路 首先,了解这样一个式子:a%m == ((b%m)*c+d)%m; 式子的正确是显然的,就不证明了. 那么判断数是否可以被13整除就可以分解为一位一位进行处理. 当然,我们也只需要储存取余后的值. dfs(len, num, mod, flag) mod记录数字对13取余后的值 len表示当前位数 num==0 不含13且上一位不为1 pre==1