数位DP应用三题

转载: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;
}

猜你喜欢

转载自blog.csdn.net/qq_36386435/article/details/81321221