数位DP UVA
来源:互联网 发布:php主要干什么 编辑:程序博客网 时间:2024/06/06 02:42
数位DP,顾名思义,是在个位,十位,百位,千位…….这些数的数位上进行的DP,它其实就是一种暴力枚举+记忆化搜索。
数位DP一般用来解决要求找出某个区间内,满足要求的数有多少个之类的问题,在这里我拿UVA - 11038来举例。这道题就是给两个整数,然后要你求出这两个整数之间的数有多少个0,比如说1到10之间有1个0,100到110之间有12个0。这是一道最基础的数位DP题,我们可以令f(a)为从0到a有多少个0,那么这道题的结果就可以表示为f(r)-f(l-1),然后我们就只需要求f(a),我们先从最高位开始枚举,比如说f(415),那么百位可以是0,1,2,3,4,当百位是0和4的时候都需要特殊考虑,如果百位是0那就表示我们目前枚举的是一个两位数,有前导零,我们可以定义一个bool lead来判断,如果百位是4,那么我们的十位数只能取0,1,因为不能超过上限,但如果百位数不是4,那么我们的十位数就可以取0到9中的任意一个,我们可以定义一个bool limit来判断。因为我们是按位枚举,所以说我们需要定义一个int pos表示当前处理的位数,有了这三个变量我们还差最后一个,也就是DP题所特有的状态变量,不同题目的状态变量是不一样的,这题因为要计算0的个数,我们可以定义状态变量为int state表示从最高位数到当前位数为止有多少个非前导0,有了这四个变量我们就可以开始写数位DP啦,每一个数位DP都需要这四个变量,状态变量在不同的题目里是不一样的,但前三个基本不会变。
下面这个模板我借鉴了WH dalao的,做了许多小改动。
typedef long long LL; int a[20]; LL dp[20][state];//不同题目状态不同 LL dfs(int pos,/*state变量*/, bool lead/*前导零*/, bool limit/*数位上界变量*/)//不是每个题都要判断前导零 { //递归边界,既然是按位枚举,最低位是1,那么pos==0说明这个数我枚举完了 if (pos == 0) return 1;/*这里一般返回1,表示你枚举的这个数是合法的,那么这里就需要你在枚举时必须每一位都要满足题目条件,也就是说当前枚举到pos位,一定要保证前面已经枚举的数位是合法的。不过具体题目不同或者写法不同的话不一定要返回1 */ //第二个就是记忆化(在此前可能不同题目还能有一些剪枝) if (!limit && !lead && dp[pos][state] != -1) return dp[pos][state]; //常规写法都是在没有限制的条件记忆化,这里与下面记录状态是对应的。 int up = limit ? a[pos] : 9;//根据limit判断枚举的上界up LL ans = 0; //开始计数 for (int i = 0; i <= up; i++)//枚举,然后把不同情况的个数加到ans就可以了 { if () ... //根据题目要求分不同情况继续进行dfs else if ()... ans += dfs(pos - 1,/*状态转移*/, lead && i == 0, limit && i == a[pos]) //最后两个变量传参都是这样写的 } //计算完,记录状态 if (!limit && !lead) dp[pos][state] = ans; /*这里对应上面的记忆化,在一定条件下时记录,保证一致性,当然如果约束条件不需要考虑lead,这里就是lead就完全不用考虑了*/ return ans; } LL solve(LL x) { int pos = 1; while (x)//把数位都分解出来 { a[pos++] = x % 10; x /= 10; } return dfs(pos - 1/*从最高位开始枚举*/,/*初始状态*/, true, true);//刚开始最高位都是有限制并且有前导零的,显然比最高位还要高的一位视为0嘛 } int main() { LL l, r; memset(dp, -1, sizeof(dp)); while (~scanf("%lld%lld", &l, &r)) { //初始化dp数组为-1 printf("%lld\n",solve(r)-solve(l-1)); } }
这个模板可以适用于许多数位DP的题目,但都要根据题目要求进行一些修改与补充,比如说这道题套用这个模板的话代码如下。
#include <iostream> #include <cstdio> #include <cstring> #include <cstdlib> #include <cmath> #include <vector> #include <queue> #include <map> #include <algorithm> #include <set> #include <functional> using namespace std; typedef long long LL; typedef unsigned long long ULL; const int INF = 1e9 + 5; const int MAXN = 50007; const int MOD = 1e8 + 7; const double eps = 1e-7; int a[50]; LL dp[50][50]; LL dfs(int pos, int state, bool lead, bool limit) { if (pos == 1)//当还剩最后一位的时候 if (lead)//如果有前导零,那就只有0这一个数 return 1; else//如果没有前导零 if (limit)//如果最后一位数有上限,当最后一位数为0的时候有1+state个0,不为0的时候有state*a[pos]个0 return 1 + state + state*a[pos]; else//如果没有上限,那么0到9一共10个state,再加上为0的时候的1个。 return 1 + state * 10; if (!limit && !lead&&dp[pos][state] != -1) return dp[pos][state]; int up = limit ? a[pos] : 9; LL ans = 0; for (int i = 0; i <= up; i++) { if (i==0&&lead)//如果有前导0的话,当前的这个0不会加入state中 { ans += dfs(pos - 1, state, lead && i == 0, limit && i == a[pos]); continue; } if (i == 0)//如果没有前导0的话,当前的这个0就会加入state中 ans += dfs(pos - 1, state + 1, lead && i == 0, limit && i == a[pos]); else ans += dfs(pos - 1, state, lead && i == 0, limit && i == a[pos]); } if (!limit && !lead) dp[pos][state] = ans; return ans; } LL solve(LL x) { int pos = 1; while (x) { a[pos++] = x % 10; x /= 10; } return dfs(pos - 1,0 , true, true);//初始的非前导0一个也没有,所以是0 } int main() { LL l, r; LL a, b; memset(dp, -1, sizeof(dp)); while (scanf("%lld%lld",&l,&r)!=EOF) { if (l == -1 && r == -1) break; if (r == 0)//0需要特判一下 a = 1; else a = solve(r); if (l == 0) b = 0; else if (l == 1)//因为l要减一,但题目里l可以是0和1,所以在这里0和1都特判一下 b = 1; else b = solve(l - 1); printf("%lld\n", a-b); } }
- uva 10401 数位DP
- uva 11361 数位DP
- uva fzu2019(数位dp)
- UVA 12670 数位DP
- 数位DP UVA
- UVA 10712 - Count the Numbers (数位DP)
- uva 417 - Word Index(数位dp)
- uva 10712 - Count the Numbers(数位dp)
- UVA 12486 Space Elevator(数位DP)
- 数位DP(越大越好,uva 12105)
- UVA 12105 Bigger is Better(数位dp)
- UVa 11361 Investigating Div-Sum Property / 数位DP
- UVA 11038 How Many O's(数位dp)
- UVA 11361 - Investigating Div-Sum Property(数位DP)
- uva 11361 - Investigating Div-Sum Property(数位dp)
- uva 1489 - Math teacher's homework(数位dp)
- UVa 11361 - Investigating Div-Sum Property (数位DP)
- UVa 12683 Odd and Even Zeroes(数论+数位DP)
- 苹果审核被拒问题总结
- 构建Visual Studio Code编译调试Linux C++环境
- Hibernate 逆向工程生成pojo----使用自定义pojo模板
- AppOps原理
- 动态规划——《3》最长公共子序列
- 数位DP UVA
- centos7 64位系统安装32位库时发生冲突
- iOS开发 iOS 10及其以上,UITextField输入时文字往下偏移问题
- 访问者模式
- Vue组件插入换行
- Cross Component Prediction
- Spring思维导图,让spring不再难懂(一)
- IOS TableView 左滑动删除Cell方法
- 开机动画,铃声添加制作