让我们来写数位DP吧~
来源:互联网 发布:政务数据开放流程 编辑:程序博客网 时间:2024/05/24 01:42
HDU 2089 不要62
限制:
62 不能连号, 不能出现4
思路:
1. 对于当前位数字是 4 有限制,可以直接处理!
2.
pre_num(前一个数字) 有限制;
if(pre_num == 6) dp当前位不能取 2 状态变化.
else dp数组可以任意取.
所以对于当前位 的 前面位有两种情况,
但是我们是写的DFS,所以先对于后面的状态先更新,所以dp更新的时候对于后一位要记录dp[][0/1] (0/1代表 pre_num(=6 / !=6 ))
int dp[10][2], a[10];int DFS(int pos, int sta, bool limit){ if(pos < 0) return 1; if(!limit && dp[pos][sta] != -1) return dp[pos][sta]; int up = limit ? a[pos] : 9; int temp = 0; for(int i=0;i<=up;i++){ if(i == 4) continue; if(sta && i == 2) continue; temp = temp + DFS(pos-1, i==6, limit && (i == up)); } if(!limit) dp[pos][sta] = temp; return temp;}int solve(int n){ int num = 0; while(n){ a[num++] = n % 10; n /= 10; } return DFS(num-1, 0, true);}int main(){ int n, m; memset(dp, -1, sizeof(dp)); while(scanf("%d%d", &n, &m)){ if(!n && !m) break; printf("%d\n", solve(m) - solve(n-1)); } return 0;}
HDU 4734 F(x);
在区间[0, B]内有多少个 F(x) <= F(A).
F(x) = An * 2n-1 + An-1 * 2n-2 + … + A2 * 2 + A1 * 1.
限制:
1. 先考虑F(x) 的最大值,最高(2^9 - 1) * 9 = 525 * 9
数位DP(大)都是从高位搜下去的,如果按照题意考虑我们持续从最高位搞个中间值加起来!
dp可以记录,当前位是多大的 个数。
但是如何转移呢???对于之前那位,OK,我已经知道之前的和 pre_sum 是多少,
现在我们只要算一下当前pos 满足 F(A) - pre_sum >= 0的那些 DP 值.
考虑减法;
如果对于当前值就是F(A),那么之后的权值和是不是<=F(A)就行了,
是不是变成了当前值是temp,那么之后的权值和是不是<=temp就行了,
那我直接把temp直接记录,就是对于当前位 <= temp 的数量.
注意:
存数位的数组一些位置无意义但被使用,要初始化!
int dp[11][5000];int a[11];int n, m;int DFS(int pos, int sta, bool limit){ if(sta < 0) return 0; if(pos < 0) return 1; if(!limit && dp[pos][sta] != -1) return dp[pos][sta]; int up = limit ? a[pos] : 9; int ans = 0; for(int i=0;i<=up;i++) ans = ans + DFS(pos-1, sta-(1<<pos)*i, limit && (i == up)); if(!limit) dp[pos][sta] = ans; return ans;}int solve(){ int num = 0; int temp = 0; while(n){ temp = temp + (n % 10) * (1<<num); n /= 10; num++; } memset(a, 0, sizeof(a)); num = 0; while(m) { a[num++] = m % 10; m /= 10; } return DFS(num-1, temp, true);}int main(){ int T, cas=1; scanf("%d", &T); memset(dp, -1, sizeof(dp)); while(T--){ scanf("%d%d", &n, &m); printf("Case #%d: %d\n", cas++, solve()); } return 0;}
51nod1009 数字1的数量
1-N的数的1的数量;
数位上的思考:
1. 个位上1的数量,不考虑个位限制,也就是意味着区间[1, 9],num0 = 1;
2. 加一个十位 1的数量,不考虑个/十位限制,也就是意味着区间[1, 99],
num1 = (两位数, 1 * 10(十位为1,个位随意[0, 9]) + 1 * 9(个位为1, 十位(1, 9))) + 1(个位数, num0 = 1)
3. 再加一个百位 1的数量,不考虑个/十/百位限制,也就是意味着区间[1, 999]
num2 = (三位数, 1 * 100(百位为1,其余随意[0, 99] + 1 * 90(十位为1, 个位[0, 9], 百位[1, 9]))) + (两位数, num1)
4. 现在直接改称 <= 4 位的一个数[1, 9999],
num4 = 1 * 1000 + 1 * 9 * 100 + 1 * 9 * 100 + 1 * 9 * 100 + num2 = 1*1000 + 3*900 + num2.
显然这还是不够的,
我们还要考虑,
like 区间[1, 36], 显然[1, 9],我们现在要知道的是[10, 36], 但是3>=1, 还是没有限制
like 区间[1, 106], 其实显然[1, 99]我们都知道了, 我们现在要知道的是[100, 106], 百位==1, 总共有6个, 十位只能是0, 个位1的时候,只能是百位为1十位是0
like 区间[1, 236], 其实显然[1, 99]我们都知道了, 我们现在要知道的是[100, 236], 百位==1, 总共有37[0, 36], 个位为1,百位可以 2 1/2, 个位可以[0, 9]
like 区间[1, 216], 其实显然[1, 99]我们都知道了, 我们现在要知道的是[100, 216], 百位==1, 总共有17[0, 16], 十位为1,百位为1, 个位可以10[0, 9], 但是百位为1, 个位只能是7[0, 6]
like 区间[1, 2345], 其实显然[1, 999]我们都知道了, 我们现在要知道[1000, 2345], 千位==1, 总共有346[0, 345],
百位为 1, 千位可以1/2, 后面就是 46 [0, 45]
总结一下???
对于这里我们可以考虑特殊情况然后暴力可以得到。
感觉方法太烂!!!
考虑递推,我们从低到高处理,
答案是累加的过程。
like 区间[1, 36]至36, 对于最高位是1, 都OK就是加[0, 9] 然后考虑尾数1的个数(统筹考虑), 那么就是 +=num1*3([0,1,2]), 之前的就是高位为3的情况
like 区间[1, 106]至106, 对于最高位是1, 那么就是 +=7[0, 06](+尾数+1), 然后加上num2*1(最高位取0)
like 区间[1, 236]至236, 对于最高位是1, 都OK就是加[0, 99], 然后考虑尾数1的个数, 那么就是 +=num2*3([0,1,2]) + 之前的就是高位为3的情况
OK!
也可以考虑DFS,考虑每一位的数的贡献。
CodeForces55D Beautiful numbers
题意:
查询区间有多少个 整除 自己每位上非0的数
限制:
0无限制、能整除每一位。
X % Y == 0
X * 10 % Y == 0
方案1.[GG]
一开始开了临时值然后直接dp[pos]巨撒比,明明这一位对于前一位是有特殊关系的,然后直接后一位求出来了,对于前一位的另一个都不满足了
like -> if(!limit && dp[pos] != -1) return dp[pos];
死于–>转移
方案2.[GG]
想了开状态来记录 [至当前] 能 (%x == 0) 的方案数,但是没用啊!!!
但是中间写崩了————对于中间的时候,很撒比地 如果 [(中间值*10 + i % i) != 0] 直接不管了!!
woc??? 你怎么知道的??? 对于中间的时候 虽然现在不能模i等于0,不代表之后加一些数可以实现膜i等于0吧
死于—>转移
总结一下————
1.显然我们是需要中间值的,用来转移
等等!这个中间好大啊!怎么优化一下,%(123456789的LCM) 就好了嘛.
2.我们无法在中间判断这个值是不是不合法!!!NO WAY!!!所以我们还要记录到个位的时候是不是对所有的位都能膜他等于0
Sooooooooo!!!我们是不是要记录前面这些位,怎么记录————乘起来很棒棒!!!————LCM更加棒棒!!!
直接存 空间复杂度好像很大!!!我们考虑LCM有多少个?(话说怎么求啊)这些LCM (1*2*3*…*8*9) 的约数嘛, 我们可以离散化一下
方案3.
现在我们数位DP, 每次会记录一个中间值temp,一个lcm, 不满足的话 就是到了最低位 temp % lcm != 0;
那么我们开 dp[][][]三维就可以刚了!
int mod, hs[3000], xs[55];void init(){ mod = 2520; int num = 0; for(int i=1;i<=mod;i++) if(mod % i == 0){ xs[++num] = i; hs[i] = num; }// printf("%d\n", num);}int a[25];LL dp[25][3000][55];int LCM(int x, int y){ return x * y / __gcd(x, y);}LL DFS(int pos, int temp, int lcm, bool limit){ if(pos < 0){ if(temp % lcm == 0) return 1; return 0; } if(!limit && dp[pos][temp][hs[lcm]] != -1) return dp[pos][temp][hs[lcm]]; int up = limit ? a[pos] : 9; int tmp, tlcm; LL ans = 0; for(int i=0;i<=up;i++){ tmp = (temp * 10 + i) % mod; if(i){ tlcm = LCM(lcm, i); ans = ans + DFS(pos-1, tmp, tlcm, limit && (i == up)); } else ans = ans + DFS(pos-1, tmp, lcm, limit && (i == up)); } if(!limit) dp[pos][temp][hs[lcm]] = ans; return ans;}LL solve(LL n){ int num = 0; mem(a, 0); while(n){ a[num++] = n % 10; n /= 10; } return DFS(num-1, 0, 1, true);}int main(){ int T; LL l, r; init(); mem(dp, -1); scanf("%d", &T); while(T--){ scanf("%lld%lld",&l, &r); printf("%lld\n", solve(r) - solve(l-1)); } return 0;}
HDU3555 Bomb
题意:
1-N到有多少个数是包含”49”的.
思路:
还记得不要62嘛…
emmmmm, 我们算一下不要49…然后哼哼哼…
LL dp[25][2];int a[25];LL DFS(int pos, int sta, bool limit){ if(pos < 0) return 1; if(!limit && dp[pos][sta] != -1) return dp[pos][sta]; int up = limit ? a[pos] : 9; LL ans = 0, t; for(int i=0;i<=up;i++){ if(sta && i == 9) continue; if(i == 4) t = 1; else t = 0; ans = ans + DFS(pos-1, t, limit && (i == up)); } if(!limit) dp[pos][sta] = ans; return ans;}LL solve(LL n){ int num = 0; mem(a, 0); while(n){ a[num++] = n % 10; n /= 10; } return DFS(num-1, 0, true);}int main(){ int T; LL n; mem(dp, -1); scanf("%d", &T); while(T--){ scanf("%lld", &n); printf("%lld\n", n - solve(n) + 1); } return 0;}
LightOJ 1140
题意:
给出一个区间,求区间内0出现的个数。
思路:
因为窝比较蠢,在想这个特殊位不就是0嘛,ok,状态设个 是0/不是0,感觉美滋滋。
但是显然,这就有一个重大的问题,是的没错,对于当前位,123456789这些数字,他的后多少位出现0的个数都没区别,
但是数位DP,这个DP记录的值表示的是对于当前位的前面所有情况!!!所以太明显了这个思路错的!
所以DP的状态 需要的是到当前位的一类情况。
那么对于这道题,记录从首位到当前位零的个数就对了。
LL dp[25][15];int a[25];LL DFS(int pos, bool pre_zero, bool limit, int sta){ if(pos < 0) return (pre_zero ? 1 : (LL)sta); if(!limit && !pre_zero && dp[pos][sta]!=-1) return dp[pos][sta]; int up = limit ? a[pos] : 9; LL temp = 0; for(int i=0;i<=up;i++){ if(pre_zero){ temp += DFS(pos-1, pre_zero&&i==0, limit&&i==up, sta); } else{ if(!i) temp += DFS(pos-1, pre_zero, limit && i==up, sta+1); else temp += DFS(pos-1, pre_zero, limit && i==up, sta); } } if(!limit && !pre_zero) dp[pos][sta] = temp; return temp;}LL solve(LL x){ if(x<0) return 0; if(x==0) return 1; int num = 0; while(x){ a[num++] = x % 10; x /= 10; } return DFS(num-1, true, true, 0);}int main(){ LL n, m; int T, cas = 1; memset(dp, -1, sizeof(dp)); scanf("%d", &T); while(T--){ scanf("%lld%lld", &n, &m); printf("Case %d: ", cas++); printf("%lld\n", solve(m) - solve(n-1)); } return 0;}
- 让我们来写数位DP吧~
- 让我们来写Sg函数处理的博弈吧~
- 让我们来帮助微软吧
- 来,让我们写一个网络爬虫,下载页面上所有的照片吧!
- 让我们用Backbone.js来写一个HelloWorld程序。
- 让我们开始用es6来写代码--gulp构建es6
- 好了,让我们来开始学习JAVA吧
- 来吧! 让我们一脚踏进FR
- 数位dp
- 数位DP
- 数位DP
- 数位dp
- 数位dp
- 数位dp
- 数位DP
- 数位dp
- 数位DP
- 【数位DP】
- 数据结构与算法·实验四
- 各种流行数据库介绍
- Java数据结构详解(十二)- HashMap
- 用户登录流程详解 +volley(StringRequest)
- 关系型数据库原理
- 让我们来写数位DP吧~
- 嵌入式多路温湿度监控系统(十51单片机温湿度采集模块)
- response与request以及编码,路径的介绍
- POJ 2586--Y2K Accounting Bug
- 编写高质量代码改善Java程序的151个建议--总结摘抄
- JAVA多线程实现的三种方式
- ssm框架数据库取数据在前台分页显示
- Xcode添加main函数输入参数
- java中创建线程的方法