HDU 2089 不要62 数位DP

时间:2022-12-16 08:54:42

预处理+递推

#include <iostream>
using namespace std ;
int f[8][10] ;//f[i][j]表示第i位是数j时符合条件的数字数量
int digit[9] ;//digit[i]表示n从右到左第i位是多少
void Init()
{
f[
0][0]=1 ;
for(int i=1 ;i<=7 ;i++)
{
for(int j=0 ;j<10 ;j++)//枚举第i位
{
for(int k=0 ;k<10 ;k++)//枚举第i-1位
{
if(!(j==6 && k==2) && j!=4)//如果符合条件
f[i][j]+=f[i-1][k] ;
}
}
}
}
int callen(int n)//计算n的长度
{
int cnt=0 ;
while(n)
{
cnt
++ ;
n
/=10 ;
}
return cnt ;
}
void caldigit(int n,int len)//计算n的digit数组
{
memset(digit,
0,sizeof(digit)) ;
for(int i=1 ;i<=len ;i++)
{
digit[i]
=n%10 ;
n
/=10 ;
}
}
int solve(int n)//计算[0,n]区间满足条件的数字个数
{
int ans=0 ;
int len=callen(n) ;
caldigit(n,len) ;
for(int i=len ;i>=1 ;i--)
{
for(int j=0 ;j<digit[i] ;j++)//枚举第i位取值
{
if(!(j==2 && digit[i+1]==6) && j!=4)//第i位满足条件
ans+=f[i][j] ;
}
if(digit[i]==4 || (digit[i]==2 && digit[i+1]==6))//第i位已经不满足条件,则i位以后都不可能满足条件,结束循环
break ;
}
return ans ;
}
int main()
{
Init() ;
int n,m ;
while(~scanf("%d%d",&n,&m))
{
if(n==0 && m==0)
break ;
printf(
"%d\n",solve(m+1)-solve(n)) ;//用[0,m]-[0,n)即可得到区间[n,m]
}
return 0 ;
}

记忆化搜索

#include <iostream>
using namespace std ;
int f[8][2] ;//dp[i][0]:前i位符合要求 dp[i][1]:前i位符合要求且i+1位是6
int digit[9] ;//digit[i]表示n从右到左第i位是多少
int dfs(int i,int s,bool e)//i表示当前位,s表示i位之前的状态,e表示当前位是否可以随意填写
{
if(i==0)
return 1 ;
if(!e && f[i][s]!=-1)
return f[i][s] ;
int res=0 ;
int u=e?digit[i]:9 ;
for(int d=0 ;d<=u ;d++)
{
if(d==4 || (s && d==2))
continue ;
res
+=dfs(i-1,d==6,e&&d==u) ;
}
return e?res:f[i][s]=res ;
}
int callen(int n)//计算n的长度
{
int cnt=0 ;
while(n)
{
cnt
++ ;
n
/=10 ;
}
return cnt ;
}
void caldigit(int n,int len)//计算n的digit数组
{
memset(digit,
0,sizeof(digit)) ;
for(int i=1 ;i<=len ;i++)
{
digit[i]
=n%10 ;
n
/=10 ;
}
}
int solve(int n)//计算[0,n]区间满足条件的数字个数
{
int len=callen(n) ;
caldigit(n,len) ;
dfs(len,
0,1) ;
}
int main()
{
int n,m ;
memset(f,
-1,sizeof(f)) ;
while(~scanf("%d%d",&n,&m))
{
if(n==0 && m==0)
break ;
printf(
"%d\n",solve(m)-solve(n-1)) ;//用[0,m]-[0,n)即可得到区间[n,m]
}
return 0 ;
}