题解HDU6148 Valley Numer(数位DP+深搜DFS)
题解HDU6148 Valley Numer[数位DP+深搜DFS]
- 题目
- 解析
- 参考源码
题目
Description:
众所周知,度度熊非常喜欢数字。
它最近发明了一种新的数字:Valley Number,像山谷一样的数字。
当一个数字,从左到右依次看过去数字没有出现先递增接着递减的“山峰”现象,就被称作 Valley Number。它可以递增,也可以递减,还可以先递减再递增。在递增或递减的过程中可以出现相等的情况。
比如,1,10,12,212,32122都是 Valley Number。
121,12331,21212则不是。
度度熊想知道不大于N的Valley Number数有多少。
注意,前导0是不合法的。
Input:
第一行为T,表示输入数据组数。
每组数据包含一个数N。
● 1≤T≤200
● 1≤length(N)≤100
e.g.
3
3
14
120
Output:
对每组数据输出不大于N的Valley Number个数,结果对 1 000 000 007 取模。
e.g.
3
14
119
解析
- TAG:
数位DP+DFS - DESCRIPITION:
山谷数是指没有出现先递增后递减的"峰"的数,给定一个大数N,求有多少个小于等于 N 的 Valley Number,模1e9+7 - INPUT:
T组测试数据(1≤T≤200),每组一个N(1≤length(N)≤100) - SOLUTION:
(1)总思路:利用dfs进行数位DP的计算与保存,dp[i][j][k]表示i位的数块前一个数是j状态为k的数有多少。状态k=0表示当前非递增,1表示当前递增。
(2)俩个特殊考虑:一个是当当前递归出来的数列与上限N重合,一个是全都是0的数不合法,此时状态pre前导值用10表示。
(3)正序或倒序处理:注意每一个数块大小为当前遍历位到最终位的长度,所以我们可以将字符串数组倒序处理,当然正序也可以,只不过dp数组表示方式有点不同,具体看代码注释。
参考源码
- 逆向递归:
//逆序递归
#include<iostream>
#include<algorithm>
#include<cstring>
#define maxn 205
using namespace std;
typedef long long ll;
const int mod = 1e9 + 7;
//dp[i][j][k]表示第i位前一个数是j状态为k的数有多少。状态0表示当前非递增,1表示当前递增
//注意虽然说是第i位,但由于是倒序的,实际上是第len-i+1位。
ll dp[maxn][15][5];
int num[maxn];
//pos表示当前在第几位(从后往前)
//status=1表示递增(从后往前)
//pre表示前面递归的是哪一位(从后往前)
//limit表示前面递归的是否
ll dfs(int pos, int status, int pre, int limit) {//过了第一位啦,1个alley Number确立,所以return 1if (pos < 1)return 1;//并且该dp状态已经存在,并且没有压边界,则可以直接调用dp值返回if (!limit && dp[pos][pre][status] != -1)return dp[pos][pre][status];//确立递归时的该位取值上限int end = limit ? num[pos] : 9;//此时状态值为ans,初始化为0ll ans = 0;if (status) {//原来是递增for (int i = pre; i <= end; i++) {//继续递增//limit && i == end表示当前面状态压线并且现在递归位也压线时接下来的递归状态也是压线ans += dfs(pos - 1, status, i, limit && i == end);ans %= mod;}}else {//原来是递减for (int i = 0; i <= end; i++) {if (i > pre) {//该位要递增ans += dfs(pos - 1, 1, i, limit && i == end);//改status=1表示原状态由非递增变为递增ans %= mod;}else {//该位要非递增//i是0且前一递归位是10,表示这是第一位赋0,因为没有前导0,00...00就不能山谷数,第一位确认0时需要赋予一个特殊状态,//即当我们在第一次遍历时需要置当前位为0时,设置一个10与非首位的0区分开来,这样最后得到一个全是10的数,结果将其减1就行if (!i && pre == 10)ans += dfs(pos - 1, 0, 10, limit && i == end);else//非首位正常递归ans += dfs(pos - 1, 0, i, limit && i == end);ans %= mod;}}}//如果不是特殊的压线情况,就保存dp值,即第pos位前一个数是pre状态为status的数有ans那么多if (!limit)dp[pos][pre][status] = ans;return ans;
}int main() {char s[maxn];int t;memset(dp, -1, sizeof(dp));cin >> t;while (t--) {cin >> s;int len = strlen(s);for (int i = 0; i < len; i++) {num[len - i] = s[i] - '0';}//pos=len,为最后一位//status=0,递减//pre=10,表示是特殊位//limit=1,最开始时要根据递归的首位选择该位上限值,所以是压线的cout << dfs(len, 0, 10, 1) - 1 << endl;}
}
- 正向递归:
//正向递归
#include<iostream>
#include<algorithm>
#include<cstring>
#define MAXN 200
using namespace std;
typedef long long ll;
const int MOD = 1e9 + 7;
//dp[i][j][k]表示i位的数块前一个数是j状态为k的数有多少。状态0表示当前非递增,1表示当前递增
ll dp[MAXN+5][15][5];
int num[MAXN+5];
int len;
//pos表示当前在第几位(从后往前)
//status=1表示递增(从后往前)
//pre表示前面递归的是哪一位(从后往前)
//limit表示前面递归的是否
ll dfs(int pos, int status, int pre, int limit) {//过了第一位啦,1个alley Number确立,所以return 1if (pos > len)return 1;//并且该dp状态已经存在,并且没有压边界,则可以直接调用dp值返回if (!limit && dp[len-pos+1][pre][status] != -1)return dp[len-pos+1][pre][status];//确立递归时的该位取值上限int end = limit ? num[pos] : 9;//此时状态值为ans,初始化为0ll ans = 0;if (status) {//原来是递增for (int i = pre; i <= end; i++) {//继续递增//limit && i == end表示当前面状态压线并且现在递归位也压线时接下来的递归状态也是压线ans += dfs(pos + 1, status, i, limit && i == end);ans %= MOD;}}else {//原来是递减for (int i = 0; i <= end; i++) {if (i > pre) {//该位要递增ans += dfs(pos + 1, 1, i, limit && i == end);//改status=1表示原状态由非递增变为递增ans %= MOD;}else {//该位要非递增//i是0且前一递归位是10,表示这是第一位赋0,因为没有前导0,00...00就不是山谷数,第一位确认0时需要赋予一个特殊状态,//即当我们在第一次遍历时需要置当前位为0时,设置一个10与非首位的0区分开来,这样最后得到一个全是10的数,结果将其减1就行if (!i && pre == 10)ans += dfs(pos + 1, 0, 10, limit && i == end);else//非首位正常递归ans += dfs(pos + 1, 0, i, limit && i == end);ans %= MOD;}}}//如果不是特殊的压线情况,就保存dp值,即第pos位前一个数是pre状态为status的数有ans那么多if (!limit) {dp[len-pos+1][pre][status] = ans;//cout << "dp[" << len - pos + 1 << "][" << pre << "][" << status << "]=" << ans << endl;}return ans;
}int main() {char s[MAXN+5];int t;memset(dp, -1, sizeof(dp));cin >> t;while (t--) {cin >> s;len = strlen(s);for (int i = 0; i < len; i++) {num[i + 1] = s[i] - '0';}//pos=1,为第一位//status=0,递减//pre=10,表示是特殊位//limit=1,最开始时要根据递归的首位选择该位上限值,所以是压线的cout << dfs(1, 0, 10, 1) - 1 << endl;}
}
题解HDU6148 Valley Numer(数位DP+深搜DFS)相关推荐
- HDU 6148 Valley Numer(数位DP)
Valley Numer Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Tot ...
- HDU 6148 Valley Numer [数位dp]
题意:求出小于等于n的,满足三种情况的任意一种的数的个数: ①每个位数字,递增,如122 ②每个位数字,递减,如211 ③每个位数字,先递减,在递增,如121 题解:数位dp,记录三维,pos(枚举的 ...
- HDU6148 Valley Numer
HDU6148 Valley Numer 题目链接 题目大意: 思路 一道很明显的数位dp,不能出现先增后减这种情况,并且前导0是不合法,所以都需要额外的标志记录状态 代码 #include < ...
- P2668 斗地主 dp+深搜版
题目描述 牛牛最近迷上了一种叫斗地主的扑克游戏.斗地主是一种使用黑桃.红心.梅花.方片的A到K加上大小王的共54张牌来进行的扑克牌游戏.在斗地主中,牌的大小关系根据牌的数码表示如下:3<4< ...
- [hdu6148][Valley Numer]
hdu6148 思路 一个数位dp模板题,注意判断前导0.用一个bz来记录当前是应该增还是可增可减.然后排除不满足条件的情况并进行dp即可. 代码 #include<cstdio> #in ...
- 深入递归、深搜dfs、回溯、剪纸学习。
深入递归,深搜dfs,回溯,剪枝 参考于博客 一.双管齐下解递归 "逐步生成结果"类问题之数值型 自下而上的递归(递推,数学归纳,动态规划) 解决简单情况下的问题. 推广到稍复杂情 ...
- acwing-167. 木棒(深搜dfs+减枝)
乔治拿来一组等长的木棒,将它们随机地砍断,使得每一节木棍的长度都不超过 50 个长度单位. 然后他又想把这些木棍恢复到为裁截前的状态,但忘记了初始时有多少木棒以及木棒的初始长度. 请你设计一个程序,帮 ...
- [二叉树|深搜|dfs] leetcode 404 左叶子之和
[二叉树|深搜|dfs] leetcode 404 左叶子之和 1.题目 题目链接 计算给定二叉树的所有左叶子之和. 示例: 3/ \9 20/ \15 7在这个二叉树中,有两个左叶子,分别是 9 和 ...
- 2412 - 和为K ---深搜dfs剪枝
**2412 - 和为K ---深搜dfs优化 **来源:东方博宜oj oj.czos.cn #include<bits/stdc++.h> using namespace std; co ...
最新文章
- android知乎多图片选择,知乎开源图片选择库 Matisse
- 3D目标检测深度学习方法数据预处理综述
- Flutter开发之数据存储-3-数据库存储(34)
- WINCE对USB HOST供电的控制
- Levenshtein Distance算法(编辑距离算法)
- 快速理解Spark Dataset
- 洛谷P1061 Jam的计数法
- Cocos2d-x游戏开发之lua编辑器 Sublime 搭建,集成cocos2dLuaApi和自有类
- h5 先加载小图_萌宝学诗|读诗、画诗、唱诗,尽在小图姐姐的《九月九日忆山东兄弟》中!...
- Nexpose漏扫使用步骤
- Web Api 返回图片流给前端
- 深度学习推荐模型-NFM
- 绝对路径、相对路径详解
- D3D游戏辅助编程开发教程
- 在职研究生-学术硕士和专业硕士有什么区别?
- 暴走湖北五城,聊聊我的湖北印象
- python创建子窗口_PyQt5实现从主窗口打开子窗口的方法
- 锐捷路由器如何配置虚拟服务器,锐捷路由器配置命令完美宝典
- reverse方向入门过程
- Chromium之工程依赖关系.