HDU 2089 數位DP 解題報告


不要62

Problem Description

杭州人稱那些傻乎乎粘嗒嗒的人為62(音:laoer)。
杭州交通管理局經常會擴充一些的士車牌照,新近出來一個好消息,以后上牌照,不再含有不吉利的數字了,這樣一來,就可以消除個別的士司機和乘客的心理障礙,更安全地服務大眾。
不吉利的數字為所有含有4或62的號碼。例如:
62315 73418 88914
都屬於不吉利號碼。但是,61152雖然含有6和2,但不是62連號,所以不屬於不吉利數字之列。
你的任務是,對於每次給出的一個牌照區間號,推斷出交管局今次又要實際上給多少輛新的士車上牌照了。

Input

輸入的都是整數對n、m(0 < n ≤ m < 1000000),如果遇到都是0的整數對,則輸入結束。

Output

對於每個整數對,輸出一個不含有不吉利數字的統計個數,該數值占一行位置。

Sample Input

1 100
0 0

Sample Output

80

【解題報告】
入門題。就是數位上不能有4也不能有連續的62,沒有4的話在枚舉的時候判斷一下,不枚舉4就可以保證狀態合法了,所以這個約束沒有記憶化的必要,而對於62的話,涉及到兩位,當前一位是6或者不是6這兩種不同情況我計數是不相同的,所以要用狀態來記錄不同的方案數。
dp[pos][sta]表示當前第pos位,前一位是否是6的狀態,這里sta只需要去0和1兩種狀態就可以了,不是6的情況可視為同種,不會影響計數。
(可以memset(dp,-1,sizeof dp);放在多組數據外面。)

代碼如下:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

int l,r;
int a[20],dp[20][2];

int dfs(int pos,int pre,int sta,bool limit)
{
if(pos==-1) return 1;
if(!limit&&dp[pos][sta]!=-1) return dp[pos][sta];
int up=limit?a[pos]:9;
int tmp=0;
for(int i=0;i<=up;++i)
{
if(pre==6&&i==2) continue;
if(i==4) continue;
tmp+=dfs(pos-1,i,i==6,limit&&i==a[pos]);
}
if(!limit) dp[pos][sta]=tmp;
return tmp;
}
int solve(int x)
{
int pos=0;
while(x)
a[pos++]=x%10,x/=10;
return dfs(pos-1,-1,0,1);
}

int main()
{
while(~scanf("%d%d",&l,&r)&&l+r)
{
memset(dp,-1,sizeof(dp));
printf("%d\n",solve(r)-solve(l-1));
}
return 0;
}

附上模板及解析

int a[20];  
ll dp[20][state];//不同題目狀態不同
ll dfs(int pos,/*state變量*/,bool lead/*前導零*/,bool limit/*數位上界變量*/)//不是每個題都要判斷前導零
{
//遞歸邊界,既然是按位枚舉,最低位是0,那么pos==-1說明這個數我枚舉完了
if(pos==-1) return 1;/*這里一般返回1,表示你枚舉的這個數是合法的,那么這里就需要你在枚舉時必須每一位都要滿足題目條件,也就是說當前枚舉到pos位,一定要保證前面已經枚舉的數位是合法的。不過具體題目不同或者寫法不同的話不一定要返回1 */
//第二個就是記憶化(在此前可能不同題目還能有一些剪枝)
if(!limit && !lead && dp[pos][state]!=-1) return dp[pos][state];
/*常規寫法都是在沒有限制的條件記憶化,這里與下面記錄狀態是對應,具體為什么是有條件的記憶化后面會講*/
int up=limit?a[pos]:9;//根據limit判斷枚舉的上界up;這個的例子前面用213講過了
ll ans=0;
//開始計數
for(int i=0;i<=up;i++)//枚舉,然后把不同情況的個數加到ans就可以了
{
if() ...
else if()...
ans+=dfs(pos-1,/*狀態轉移*/,lead && i==0,limit && i==a[pos]) //最后兩個變量傳參都是這樣寫的
/*這里還算比較靈活,不過做幾個題就覺得這里也是套路了
大概就是說,我當前數位枚舉的數是i,然后根據題目的約束條件分類討論
去計算不同情況下的個數,還有要根據state變量來保證i的合法性,比如題目
要求數位上不能有62連續出現,那么就是state就是要保存前一位pre,然后分類,
前一位如果是6那么這意味就不能是2,這里一定要保存枚舉的這個數是合法*/

}
//計算完,記錄狀態
if(!limit && !lead) dp[pos][state]=ans;
/*這里對應上面的記憶化,在一定條件下時記錄,保證一致性,當然如果約束條件不需要考慮lead,這里就是lead就完全不用考慮了*/
return ans;
}
ll solve(ll x)
{
int pos=0;
while(x)//把數位都分解出來
{
a[pos++]=x%10;//個人老是喜歡編號為[0,pos),看不慣的就按自己習慣來,反正注意數位邊界就行
x/=10;
}
return dfs(pos-1/*從最高位開始枚舉*/,/*一系列狀態 */,true,true);//剛開始最高位都是有限制並且有前導零的,顯然比最高位還要高的一位視為0嘛
}
int main()
{
ll le,ri;
while(~scanf("%lld%lld",&le,&ri))
{
//初始化dp數組為-1,這里還有更加優美的優化,后面講
printf("%lld\n",solve(ri)-solve(le-1));
}
}

注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
粤ICP备14056181号  © 2014-2020 ITdaan.com