数位DP—— GDUT 等凹数字

本文介绍了一种求解特定区间内等凹数字数量的方法。等凹数字是一种特殊的回文数,其数值特点是从高位到低位先非递增再非递减,并形成单一等凹峰。文中详细解释了如何通过状态定义和深度优先搜索策略来实现这一算法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

  • 题目链接: http://gdutcode.sinaapp.com/problem.php?cid=1057&pid=6

  • 题意:求出区间内等凹数字的个数,等凹数字:定义一种数字称为等凹数字,即从高位到地位,每一位的数字先非递增再非递减,不能全部数字一样,且该数是一个回文数,即从左读到右与从右读到左是一样的,仅形成一个等凹峰。

  • 分析:解析一个等凹数字需要满足的条件:必须下降过,必须上升过,必须是回文。那么我们就可以定义出前缀的状态:剩下数位,等凹数长度,前缀末尾的数字,前缀是否下降过,前缀是否上升过,前缀是否为回文。然后搜索出所有前缀含有的等凹数的数量。

  • AC代码:

/*************************************************************************
    > File Name: test.cpp
    > Author: Akira 
    > Mail: qaq.febr2.qaq@gmail.com 
 ************************************************************************/

#include<bits/stdc++.h>
typedef long long LL;
typedef unsigned long long ULL;
typedef long double LD;
#define MST(a,b) memset(a,b,sizeof(a))
#define CLR(a) MST(a,0)
#define Sqr(a) ((a)*(a))
using namespace std;

#define MaxN 100001
#define MaxM MaxN*10
#define INF 0x3f3f3f3f
#define PI 3.1415926535897932384626
const int mod = 1E9+7;
const double eps = 1e-6;
#define bug cout<<88888888<<endl;
#define debug(x) cout << #x" = " << x<<endl;
int T;
LL L,R;
int digits[20];
int num[20];
LL DP[20][20][10][2][2][2];
LL dfs(int pos, int len, int pre, bool up, bool down, bool ispa, bool zero, bool limit)
{
    if(pos==0) return up&&down&&ispa;
    if(!limit && DP[pos][len][pre][up][down][ispa]!=-1) return DP[pos][len][pre][up][down][ispa];
    LL ans = 0;
    int Max = limit ? digits[pos] : 9;
    for(int i=0;i<=Max;i++)
    {
        num[pos] = i; //深搜路上记录每次选择的数字
        if(zero) ans += dfs(pos-1, len-(i==0), i, 0, 0, ispa, (i==0), limit&&i==Max);
        else if(i==pre)
        {
            if(ispa&&pos<=len/2) ans += dfs(pos-1,len,i,up,down,i==num[len+1-pos],zero,limit&&i==Max);
            else ans += dfs(pos-1,len,i,up,down,ispa,zero,limit&&i==Max);
        }
        else if(i<pre)
        {
            if(up) continue;    //如果上升过,就不能下降
            if(ispa&&pos<=len/2) ans += dfs(pos-1,len,i,up,true,i==num[len+1-pos],zero,limit&&i==Max);
            else ans += dfs(pos-1,len,i,up,true,ispa,zero,limit&&i==Max);
        }
        else if(i>pre)
        {
            if(!down) continue; //如果没有下降过,不能上升
            if(ispa&&pos<=len/2) ans += dfs(pos-1,len,i,true,down,i==num[len+1-pos],zero,limit&&i==Max);
            else ans += dfs(pos-1,len,i,true,down,ispa,zero,limit&&i==Max);
        }
    }
    if(!limit) DP[pos][len][pre][up][down][ispa]=ans;
    return ans;
}

LL find(LL x)
{
    int pos = 0;
    while(x)
    {
        digits[++pos] = x%10;
        x/=10;
    }
    return dfs(pos,pos,0,false,false,true,true,true);
}

void solve()
{
    printf("%lld\n", find(R)-find(L-1));
}
void init()
{
    MST(DP,-1);
}
int main()
{
    init();
    //std::ios::sync_with_stdio(false);
    scanf("%d", &T);
    while(T--)
    {
        scanf("%lld%lld", &L, &R);
        solve();
    }
    //system("pause");
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值