转载:https://blog.csdn.net/brazy/article/details/77427699
https://www.cnblogs.com/zbtrs/p/6106783.html
例一、Valley Numer
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 948 Accepted Submission(s): 498
Problem 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
Output
对每组数据输出不大于N的Valley Number个数,结果对 1 000 000 007 取模。
Sample Input
3
3
14
120
Sample Output
3
14
119
Source
2017百度之星程序设计大赛 - 复赛
数位DP题的关键在于如何分析下一个数字的情况
利用pre和turn来描述数字的状态(像前面做过的题,dp[len][if6]就是利用前一位是不是6来确定状态)
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
typedef long long LL;
const int inf = 0x3f3f3f3f;
const int N = 105;
const int mod = 1e9 + 7;
char s[N];
int a[N];
LL dp[N][10][3];
//turn = 0表示不清楚 turn = 1 表示上升 turn = 2 表示下降
LL dfs(int pos,int pre,int turn,bool shangxian,bool invalid)
{
if(pos == -1){
return invalid ? 0 : 1;
}
if(!shangxian && dp[pos][pre][turn] != -1){
return dp[pos][pre][turn];
}
LL ans = 0;
int maxn = shangxian ? a[pos] : 9;
for(int i = 0;i <= maxn;++i)
{
if(turn == 1 && i < pre){
continue;
}
int p = 0;
if(i == pre){
p = turn;
}else if(i < pre){
p = 2;
}else{
p = 1;
}
if(invalid){
p = 0;
}
ans += dfs(pos - 1,i,p,shangxian && i == a[pos],invalid && i == 0);
ans %= mod;
}
ans %= mod;
if(!shangxian){
dp[pos][pre][turn] = ans;
}
return ans;
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
scanf("%s",s);
int len = strlen(s);
memset(dp,-1,sizeof(dp));
for(int i = 0;i < len;++i)
{
a[i] = s[len - 1 - i] - '0';
}
printf("%lld\n",dfs(len - 1,0,0,true,true) % mod);
}
return 0;
}
例题二:找出1~n范围内含有13并且能被13整除的数字的个数.
分析:和例1相比多了一个整除,怎么处理呢?其实只需要在记忆化搜索中增加一个参数mod即可,利用(a * b) % mod = (a % mod) * (b % mod)和(a + b) % mod = (a % mod) + (b % mod)来计算.比如说73 % 10 = ((7 % 10) * 10 + 3) % 10,但要注意本题是要找含有13的数,那么在处理的时候就要进行分类讨论.
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
int n, shu[20], dp[20][20][10];
int dfs(int len, int mod, int zhuangtai, bool shangxian)
{
if (len == 0)
return mod == 0 && zhuangtai == 2;
if (!shangxian && dp[len][mod][zhuangtai])
return dp[len][mod][zhuangtai];
int cnt = 0, maxx = (shangxian ? shu[len] : 9);
for (int i = 0; i <= maxx; i++)
{
int tz = zhuangtai;
if (zhuangtai != 2 && i != 1)
tz = 0;
if (zhuangtai == 1 && i == 3)
tz = 2;
if (i == 1 && zhuangtai != 2)
tz = 1;
cnt += dfs(len - 1, (mod * 10 + i) % 13, tz, shangxian && i == maxx);
}
if (!shangxian)
dp[len][mod][zhuangtai] = cnt;
return cnt;
}
int main()
{
while (~scanf("%d", &n))
{
memset(shu, 0, sizeof(shu));
memset(dp, 0, sizeof(dp));
int k = 0;
while (n)
{
shu[++k] = n % 10;
n /= 10;
}
printf("%d\n", dfs(k, 0, 0, 1));
}
return 0;
}
例三、
BZOJ1026: [SCOI2009]windy数
Time Limit: 1 Sec Memory Limit: 162 MB
Submit: 6392 Solved: 2854
Description
windy定义了一种windy数。不含前导零且相邻两个数字之差至少为2的正整数被称为windy数。 windy想知道,
在A和B之间,包括A和B,总共有多少个windy数?
Input
包含两个整数,A B。
Output
一个整数
Sample Input
【输入样例一】
1 10
【输入样例二】
25 50
Sample Output
【输出样例一】
9
【输出样例二】
20
HINT
【数据规模和约定】
100%的数据,满足 1 <= A <= B <= 2000000000 。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
using namespace std;
int a, b,num[20],dp[20][12];
int dfs(int len, int last, bool shangxian)
{
int p;
if (len <= 0)
return 1;
if (!shangxian && dp[len][last] != -1&& last >= 0)
return dp[len][last];
int cnt = 0, maxx = (shangxian ? num[len] : 9);
for (int i = 0; i <= maxx; i++)
{
if (abs(i - last) < 2)
continue;
p = i;
if (i == 0 && last == -10)
p = last;
cnt += dfs(len - 1, p, shangxian && (i == maxx));
}
//return cnt;
if (last >= 0 && !shangxian)
dp[len][last] = cnt;
return cnt;
}
int solve(int x)
{
int k = 0;
while (x)
{
num[++k] = x % 10;
x /= 10;
}
memset(dp, 255, sizeof(dp));
return dfs(k, -10, true);
}
int main()
{
scanf("%d%d", &a, &b);
printf("%d\n", solve(b) - solve(a - 1));
return 0;
}