题目:
题意:给定一个区间[a,b],问在这个区间中的数字,不包含4和62的数有多少个?
分析:数据范围小于1e6,如果暴力的话,再加上分解因数,预处理的时间复杂度数量级最大1e7,查询O(1)。这题主要是学习一下数位dp。
暴力:
#include<iostream>
#include<cstdio>
using namespace std;
typedef long long ll;
const int N=1000000+5;
int f[N];
bool check(int x)
{
while(x){
int t=x%10;
x/=10;
if(t==4||t==2&&x%10==6)return false;
}
return true;
}
void init()
{
f[0]=0;
for(int i=1;i<1000001;i++){
if(check(i))f[i]=f[i-1]+1;
else f[i]=f[i-1];
}
}
int main()
{
int a,b;
init();
while(~scanf("%d%d",&a,&b)&&(a+b)){
printf("%d\n",f[b]-f[a-1]);
}
return 0;
}
数位dp:关键是怎么想到状态方程以及如何转移。
#include <iostream>
#include <cstdio>
using namespace std;
int f[10][10];//f[i][j]表示开头是j的i位数中不含62或4的数有几个
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!=4&&!(j==6&&k==2))
f[i][j] += f[i-1][k];
}
}
}
}
int solve(int n)
{
int digit[10];
int len = 0;
while(n>0)
{
digit[++len] = n%10;
n/=10;
}
digit[len+1]=0;
int ans = 0;
for(int i=len;i;i--)//枚举哪一位<n的对应位
{
for(int j=0;j<digit[i];j++)//枚举这一位的取值
{
if(j!=4&&!(digit[i+1]==6&&j==2))
ans+=f[i][j];
//cout<<f[i][j]<<endl;
}
if(digit[i]==4||(digit[i]==2&&digit[i+1]==6))//已经出现4或62
break;
}
return ans;
}
int main()
{
int a,b;
init();
while(~scanf("%d%d",&a,&b)&&(a+b)){
printf("%d\n",solve(b+1)-solve(a));
}
return 0;
}
初探数位dp:点击打开链接
又学习了一个很好的方法,注释在代码中
分析:从高位到低位搜一遍,f[i][j],中间记录i位的情况,判断是否合法,合法则加入ans。
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int f[8][2],bit[8];
/*
f[i][j]:i是位数
j==0,尾位不是6
j==1,尾位是6
*/
int dfs(int pos,bool six,bool lim)//位数,尾位是否是6,是否是上限
{
if(pos<=0)return 1;
if(!lim&&f[pos][six]!=-1)return f[pos][six];
int num=lim?bit[pos]:9; //假设该位是2,下一位是3,如果现在算到该位为1,那么lim=0,表示下一位是能取到9的,
//如果该位为2,那么lim=1,下一位只能取到3
int ans=0;
for(int i=0;i<=num;i++){
if(i==4||six&&i==2)continue;//如果再加一位是4,或者尾位是6再加一位是2,那么都是不合法的状态,跳过。
ans+=dfs(pos-1,i==6,lim&&i==num);
}
if(!lim)f[pos][six]=ans;
return ans;
}
int solve(int n)
{
int len=0;
memset(f,-1,sizeof(f));
while(n){
bit[++len]=n%10;
n/=10;
}
return dfs(len,0,1);
}
int main()
{
int a,b;
while(~scanf("%d%d",&a,&b)&&(a+b)){
printf("%d\n",solve(b)-solve(a-1));
}
return 0;
}