HDU 3565 数位DP + 上下界

89 篇文章 1 订阅

数位DP + 上下界

题意:

​ 定义双峰数:先上升后下降,若一个数字中恰好存在两个这样的状态就计算这个数字的所有位数和。给出一个区间,求出最大的和。(每一个峰不予许出现前导0)

思路:

​ 数位DP一般给出一个范围,从0到某一个数字,而这个题不行,因为数字有限制,有左区间。所以要做的就是枚举范围内的每一数字的状态,当然直接暴力是不可能的,而数字不超过30位,所以枚举数字,而限制可以通过上下界来确定e1,e2.

​ 定义: dp[pos][fa][ok] 在pos+1位数字是fa而状态是ok 的总的双峰数。

为什么要有状态呢?只有知道当前的状态才知道下一步要做什么,根据双峰:低高低低高低有6个状态,当然要特判0的存在,只要最后达到了最后的状态就算一种情况,可以返回此时状态的位数和,取最大值即可。当-1的时候要特判,因为有可能区间没有双峰数。

#include <iostream>
#include <cstdio>
#include <cstring>

using namespace std;

#define ll unsigned __int64

int dp[30][10][7];
int bit1[30],bit2[30];

int dfs(int pos,int fa,int ok,int e1,int e2)
{
    if(pos == 0) return ok == 6 ? 0 : -1;
    if(!e1 && !e2 && ~dp[pos][fa][ok]) return dp[pos][fa][ok];
    int Min = e1 ? bit1[pos] : 0;
    int Max = e2 ? bit2[pos] : 9;
    int ans = -1;
    for(int i = Min;i <= Max; i++) {
        int next = 0;
        if(ok == 0 && i) {
             next = 1;
        }
        else if(ok == 1) {
            if(i > fa) next = 2;
            else next = -1;
        }
        else if(ok == 2) {
            if(i > fa) next = 2;
            else if(i < fa) next = 3;
            else next = -1;
        }
        else if(ok == 3) {
            if(i > fa) next = 4;
            else if(i == fa) {
                if(i) next = 4;
                else next = -1;
            }
            else next = 3;
        }
        else if(ok == 4) {
            if(i > fa) next = 5;
            else next = -1;
        }
        else if(ok == 5) {
            if(i > fa) next = 5;
            else if(i < fa) next = 6;
            else next = -1;
        }
        else if(ok == 6) {
            if(i < fa) next = 6;
            else next = -1;
        }
        if(next != -1) {
            int sum = dfs(pos-1,i,next,e1&&i==Min,e2&&i==Max);
            if(sum != -1)
                ans = max(ans,sum + i);
        }
    }
    if(!e1 && !e2) dp[pos][fa][ok] = ans;
    return ans;
}

int main(int argc, char const *argv[])
{
    // freopen("in.txt","r",stdin);

    memset(dp,-1,sizeof(dp));
    int tt,ncase = 1;
    scanf("%d",&tt);
    while(tt--) {
        ll l,r;
        scanf("%I64u%I64u",&l,&r);
        int pos = 0;
        while(r) {
            pos++;
            bit1[pos] = l%10;
            l /= 10;
            bit2[pos] = r%10;
            r /= 10;
        }
        int ans = dfs(pos,0,0,1,1);
        printf("Case %d: %d\n",ncase++,ans==-1?0:ans);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值