2017广东工业大学程序设计竞赛决赛 Problem G: 等凹数字(回文+数位dp)

Problem G: 等凹数字
Description
定义一种数字称为等凹数字,即从高位到地位,每一位的数字先非递增再非递减,不能全部数字一样,且该数是一个回文数,即从左读到右与从右读到左是一样的,仅形成一个等凹峰,如543212345,5544334455是合法的等凹数字,543212346,123321,111111不是等凹数字。现在问你[L,R]中有多少等凹数字呢?

Input
第一行一个整数T,表示数据的组数。
接下来T行每行俩个数字L和R,(1<=L<=R<=1e18)
Output
输出一个整数,代表[L,R]中有多少等凹数字
Sample Input
2
1 100
101 200
Sample Output
0
1
HINT

小于等于2位的数字无凹峰

套上回文数位dp模板,加上4个维度(上升,下降,前导0,前一个数)看了一下别人解题思路,知道多开这几维才写出来QAQ
代码里有自己的注释
AC代码

#include<stdio.h>
#include<string.h>
int degth[20];
int temp[20];
long long dp[20][20][10][3][3][3];
long long dfs(int pos,int len,int pre,int up,int down,int flag,int f,int k)//长度,当前位,前一位,上升,下降,回文 
{
    if(len<0) return up&&down&&flag; //上升加下降加是回文 
    if(!f&&dp[pos][len][pre][up][down][flag]!=-1&&!k)
    return dp[pos][len][pre][up][down][flag];
    int max=f?degth[len]:9;
    long long ret=0;
    for(int i=0;i<=max;i++)
    {
        temp[len]=i;
        if(k) //前导0时直接降低长度,否则执行下一位(因为起始位无法判断上升和下降) 
        ret+=dfs(pos-(k&&i==0),len-1,i,0,0,flag,f&&i==max,k&&i==0);
        else if(pre==i)
        {
            if(flag&&(pos+1)/2>len)
                ret+=dfs(pos,len-1,i,up,down,i==temp[pos-len],f&&i==max,k&&i==0); 
            else ret+=dfs(pos,len-1,i,up,down,flag,f&&i==max,k&&i==0);
        }
        else if(pre<i)//递增 
        {
            if(!down) continue; //没有下降就上升
            if(flag&&(pos+1)/2>len)
                ret+=dfs(pos,len-1,i,1,down,i==temp[pos-len],f&&i==max,k&&i==0);
            else ret+=dfs(pos,len-1,i,1,down,flag,f&&i==max,k&&i==0);
        }
        else if(pre>i)//递减
        {
            if(up) continue;//已经上升过
            if(flag&&(pos+1)/2>len)
                ret+=dfs(pos,len-1,i,up,1,i==temp[pos-len],f&&i==max,k&&i==0);
            else ret+=dfs(pos,len-1,i,up,1,flag,f&&i==max,k&&i==0);
        } 
    }
    if(!f&&!k) dp[pos][len][pre][up][down][flag]=ret;
    return ret;
}
long long solve(long long x)
{
    int len=0;
    while(x)
    {
        degth[len++]=x%10;
        x/=10;
    }
    return dfs(len-1,len-1,0,0,0,1,1,1);
}
int main()
{
    memset(dp,-1,sizeof(dp));
    int T;
    scanf("%d",&T);
    while(T--)
    {
        long long l,r;
        scanf("%lld%lld",&l,&r);
        printf("%lld\n",solve(r)-solve(l-1));
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值