【数位DP】HDU6148[Valley Number]题解

题目概述

一个正整数是 Valley Number 需要满足没有任何一段数字出现先递增后递减(这里的递增和递减都不是严格的)的情况。

n Valley Number 的数量。

解题报告

显然是数位DP,但是我刚开始想了一个很有病的定义,样例都没调出来TAT。

定义 f[i][j][t] 表示前 i 个数中第 i 个数是 j 且当前状态是 t 0 :递减, 1 :递增)的方案数。

先预处理出 Sum(i) 表示长度为 1 ~ i Valley Number 总数,然后我们只需要求长度为 Length(n) Valley Number 就行了。

示例程序

请读者老爷原谅我这么乱的代码(数位DP代码总是很乱),DP(L,R)表示求 f[L] ~ f[R]

#include<cstdio>
#include<cstring>
using namespace std;
const int maxn=100,MOD=1000000007;

int te,n,a[maxn+5],f[maxn+5][10][2],sum[maxn+5];

#define Eoln(x) ((x)==10||(x)==13||(x)==EOF)
int check(int k,int t,int j)
{
    if (t) if (k>j) return -1; else return 1;
    if (!t) if (k<j) return 1; else return 0;
}
void AMOD(int &x,int tem) {x+=tem;if (x>MOD) x-=MOD;}
void DP(int L,int R)
{
    for (int i=L;i<=R;i++)
    for (int k=0;k<=9;k++)
    for (int t=0;t<=1;t++) if (f[i-1][k][t])
    for (int j=0;j<=9;j++)
    {
        int x=check(k,t,j);if (x==-1) continue;
        AMOD(f[i][j][x],f[i-1][k][t]);
    }
}
void Make()
{
    for (int i=1;i<=maxn;i++)
    {
        sum[i]=sum[i-1];memset(f,0,sizeof(f));
        for (int j=1;j<=9;j++) f[1][j][0]=1;DP(2,i);
        for (int j=0;j<=9;j++) AMOD(sum[i],f[i][j][0]),AMOD(sum[i],f[i][j][1]);
    }
}
int main()
{
    freopen("program.in","r",stdin);
    freopen("program.out","w",stdout);
    for (Make(),scanf("%d",&te);te;te--)
    {
        char ch=getchar();while (Eoln(ch)) ch=getchar();
        n=0;while (!Eoln(ch)) a[++n]=ch-48,ch=getchar();
        int ans=sum[n-1];memset(f,0,sizeof(f));
        for (int j=1;j<a[1];j++) f[1][j][0]=1;DP(2,n);
        for (int j=0;j<=9;j++) AMOD(ans,f[n][j][0]),AMOD(ans,f[n][j][1]);
        int fl=0;
        for (int p=1;p<n;p++)
        {
            if (p>1) fl=check(a[p-1],fl,a[p]);if (fl==-1) break;
            memset(f,0,sizeof(f));f[p][a[p]][fl]=1;
            for (int j=0,x;j<a[p+1];j++) if (~(x=check(a[p],fl,j))) f[p+1][j][x]=1;
            DP(p+2,n);for (int j=0;j<=9;j++) AMOD(ans,f[n][j][0]),AMOD(ans,f[n][j][1]);
        }
        if (~fl&&n>1) fl=check(a[n-1],fl,a[n]);if (~fl) AMOD(ans,1);
        printf("%d\n",ans);
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值