『8.21 模拟赛』Victory

Posted fang-hao

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了『8.21 模拟赛』Victory相关的知识,希望对你有一定的参考价值。

题目描述

迟到大王rsw喜欢V这个字母,因为V代表着victory,当一个数字,从左向右数的时候,没有出现过先递减,再递增的情况,就被称作Victory数。

也就是说,这个数字可以是递增的,也可以是递减的,也可以是先递减再递增的,这个过程中可以出现相邻数字相等的情况,但是,就是不能出现过先递减再递增的情况。

问题是:给定n,问:1~n之间有多少个victory数。





解题思路

我们应该一眼就能看出这是一道数位dp题,一开始用3维dp写,状态不够写挂了。。。后来改用4维就A了。

我们用dp[ i ][ j ][ k ][ l ] 来表示当前选到了第i位,当前选择的数是j,(k=0)表示当前的状态是递增的,(k=1)表示当前的状态是递减的,(l=0)表示当前没有遇到上界,(l=1)表示当前遇到了上界。但是在转移的时候有很多的细节,所以我们要慢慢分析:

比如,上升和下降并不是严格的,所以我们一定要注意一下,比如还没有出现严格上升的时候的k一直等于1,也就是递减的,遇到了一次严格递增的我们就把k严格设置成0,这样我们就可以防止重复情况的计算。

我们dfs的时候一定要注意前导0对答案的影响:比如098是一个合法的三位数,但是我们很容易漏掉他,一开始在这挂了好久,我们从每一位开始枚举,注意枚举最高位的时候l的取值。





代码

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#define LL long long
using namespace std;
const LL MOD=1000000007;
char n[150];
int len,a[150];
LL dp[102][11][2][2];
inline LL dfs(int wei,int num,bool ud,bool flag){
    if(wei==1)return 1;
    if(dp[wei][num][ud][flag])return dp[wei][num][ud][flag];
    if(ud==0){
        if(flag){
            for(register int i=num;i<=a[wei-1];i++){
                if(i==a[wei-1]){
                    dp[wei][num][ud][flag]+=dfs(wei-1,i,ud,1);
                    dp[wei][num][ud][flag]%=MOD;
                }
                else {
                    dp[wei][num][ud][flag]+=dfs(wei-1,i,ud,0);
                    dp[wei][num][ud][flag]%=MOD;
                }
            }
        }
        else {
            for(register int i=num;i<=9;i++){
                dp[wei][num][ud][flag]+=dfs(wei-1,i,ud,0);
                dp[wei][num][ud][flag]%=MOD;
            }
        }
        return dp[wei][num][ud][flag];
    }
    else {
        if(flag){
            for(register int i=0;i<=min(num,a[wei-1]);i++){
                if(i==a[wei-1]){
                    dp[wei][num][ud][flag]+=dfs(wei-1,i,ud,1);
                    dp[wei][num][ud][flag]%=MOD;
                }
                else {
                    dp[wei][num][ud][flag]+=dfs(wei-1,i,ud,0);
                    dp[wei][num][ud][flag]%=MOD;
                }
            }
            for(register int i=num+1;i<=a[wei-1];i++){
                if(i==a[wei-1]){
                    dp[wei][num][ud][flag]+=dfs(wei-1,i,0,1);
                    dp[wei][num][ud][flag]%=MOD;
                }
                else {
                    dp[wei][num][ud][flag]+=dfs(wei-1,i,0,0);
                    dp[wei][num][ud][flag]%=MOD;
                }
            }
        }
        else {
            for(register int i=0;i<=num;i++){
                dp[wei][num][ud][flag]+=dfs(wei-1,i,1,0);
                dp[wei][num][ud][flag]%=MOD;
            }
            for(register int i=num+1;i<=9;i++){
                dp[wei][num][ud][flag]+=dfs(wei-1,i,0,0);
                dp[wei][num][ud][flag]%=MOD;
            }
        }
        return dp[wei][num][ud][flag];
    }
}
int main(){
    scanf("%s",n+1);
    len=strlen(n+1);
    for(register int i=1;i<=len;i++){
        a[len+1-i]=(int)(n[i]-'0');
    }
    LL ans=0;
    for(register int i=1;i<=a[len];i++){
        if(i!=a[len])ans+=dfs(len,i,1,0);
        else ans+=dfs(len,i,1,1);
        ans%=MOD;
    }
    for(register int i=len-1;i>=1;i--){
        memset(dp,0,sizeof(dp));
        for(register int j=1;j<=9;j++){
            ans+=dfs(i,j,1,0);
            ans%=MOD;
        }
    }
    cout<<ans<<endl;
}

以上是关于『8.21 模拟赛』Victory的主要内容,如果未能解决你的问题,请参考以下文章

『8.21 模拟赛』冒泡排序 II

「模拟8.21」虎

「模拟8.21」山洞(矩阵优化DP)

8.21

上周热点回顾(8.21-8.27)

CF61DEternal Victory