題目鏈接:HDU 3565
題目大意:定義“雙峯數”爲滿足可以分割成兩個 /\ /\ 的形式的數,求區間[L,R]內雙峯數位數和的最大值。
題解:第一次寫數位DP,開篇BLOG記錄一下 ○(>_<)○
用記憶化搜索實現,dfs(int wei,int cur,int sta,bool fdn,bool fup)
表示當前填到第wei位,上一位數字是cur,雙峯數的狀態爲sta,這一位的取值是(true)否(false)可能頂到上(fdn)下(fup)界時已經填的數位和最大值。
這裏sta=0~6分別表示:0還沒有開始填,1走到了第一個峯的上坡但是下一步不能直接向下走,2走到了第一個峯的上坡並且下一步可以向上也可以向下走,3走到了第一個峯的下坡處,4走到了第二個峯的上坡但是下一步不能直接向下走,5並且下一步可以向上也可以向下走,6走到了第二個峯的下坡並且之後之能向下走。大概是下面的樣子:
_2_ _5_
/ \ / \
1/ \3 4/ \6
/ \ / \
0 (_) \
轉移的時候枚舉下一位填什麼數(注意上下界),判斷雙峯數的狀態,沒有頂到上下界的部分可以記憶化。代碼在下面:
code
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
typedef unsigned long long ll;
inline ll read()
{
char c=getchar(); ll num=0,f=1;
while (c<'0'||c>'9') { if (c=='-') f=-1; c=getchar(); }
while (c<='9'&&c>='0') { num=num*10+c-'0'; c=getchar(); }
return num*f;
}
ll T,L,R;
int dp[21][11][7],r[21],l[21];
int dfs(int wei,int cur,int sta,bool fdn,bool fup)
{
if (wei==0) return (sta==6)?0:-1;
if (!fdn&&!fup&&(~dp[wei][cur][sta])) return dp[wei][cur][sta];
int mn=(fdn?l[wei]:0),mx=(fup?r[wei]:9),ret=-1;
for (int i=mn;i<=mx;i++)
{
int tmp;
switch (sta)
{
case 0: tmp=i?1:0; break;
case 1: tmp=(i>cur)?2:-1; break;
case 2: tmp=(i>cur)?2:((i<cur)?3:-1); break;
case 3: tmp=(i<cur)?3:4; break;
case 4: tmp=(i>cur)?5:-1; break;
case 5: tmp=(i>cur)?5:((i<cur)?6:-1); break;
case 6: tmp=(i<cur)?6:-1; break;
}
if (~tmp)
{
int num=dfs(wei-1,i,tmp,fdn&&i==mn,fup&&i==mx) ;
if (~num) ret=max(ret,num+i);
}
}
if (!fdn&&!fup) dp[wei][cur][sta]=ret;
return ret;
}
int main()
{
T=read(); memset(dp,-1,sizeof(dp));
for (int i=1;i<=T;i++)
{
L=read(); R=read(); l[0]=r[0]=0;
while (R)
{
l[++l[0]]=L%10,L/=10;
r[++r[0]]=R%10,R/=10;
}
int ans=dfs(r[0],0,0,1,1); if (ans==-1) ans=0;
printf("Case %d: %d\n",i,ans);
}
return 0;
}