hdu 3555 Bomb
这个题目是最初级的数位dp题目了
递推的形式:
dp1[ i ] 表示有i个自由位含有49的个数
dp2[ i ] 表示有i个自由位以9开头不含49的个数
dp3[ i ] 表示有i个自由不以9开头且不含49的个数
要注意的是递推计算的是[0 , n-1] 范围内的数,所以n要++,为什么看程序注释
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
typedef long long ll;
ll dp1[20],dp2[20],dp3[20],digit[20];
void init()
{
dp3[0]=1;
dp1[1]=0,dp2[1]=1,dp3[1]=9;
for(int i=2;i<20;i++)
{
dp1[i]=10*dp1[i-1]+dp2[i-1];
dp2[i]=dp2[i-1]+dp3[i-1];
dp3[i]=8*dp2[i-1]+9*dp3[i-1];
}
//cout<<dp1[2]<<" "<<dp2[2]<<" "<<dp3[2]<<endl;
}
int main()
{
init();
ll n;
int T;
scanf("%d",&T);
while(T--)
{
scanf("%I64d",&n);
int tot=0;
for(;n;n/=10) digit[tot++]=n%10;
int last=0,flag=0;
ll ans=0;
for(int i=tot-1;i>=0;i--)
{
for(int j=0;j<digit[i];j++)
{
//if(i==0) cout<<j<<" ";
ans+=dp1[i];
if(flag) ans+=dp2[i]+dp3[i];
else if(j==4) ans+=dp2[i];
}
if(last==4&&digit[i]==9) flag=1;
last=digit[i]; // 当last只要一等于digit[0]的时候程序就退出了,所以没有判断n是否满足条件
// if(i==1) cout<<flag<<endl;
}
printf("%I64d\n",ans);
}
return 0;
}
还有就是记忆化搜索的形式实现的,有一点要注意的是dfs传递的形参一定会把完整的状态表示出来,不然没法转移
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
typedef long long ll;
ll dp[20][3];
int digit[20];
ll dfs(int pos,int state,bool doing)
{
if(pos<0) return state==2;
if(!doing&&dp[pos][state]!=-1) return dp[pos][state];
ll ans=0;
int end=doing?digit[pos]:9;
for(int i=0;i<=end;i++)
{
int ns=state;
if(ns==1&&i==9) ns=2;
if(ns==1&&i!=4) ns=0;
if(ns==0&&i==4) ns=1;
ans+=dfs(pos-1,ns,doing&&i==end);
}
if(!doing) dp[pos][state]=ans;
return ans;
}
int main()
{
memset(dp,-1,sizeof(dp));
ll n;
int T;
scanf("%d",&T);
while(T--)
{
scanf("%I64d",&n);
int tot=0;
for(;n;n/=10) digit[tot++]=n%10;
printf("%I64d\n",dfs(tot-1,0,1));
}
return 0;
}