数位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;
}