题目链接:http://115.28.76.232/problem?pid=1064
题目大意:给定一个区间,找出在这个区间内,数位里有3或者有8但不能同时同时有3或8的数的个数
题目类型:数位dp
此题有两个版本的写法,时间复杂度一样,但是代码长度差很多
第一种是用dfs写的,代码如下:
#include <cstdio>
#include <iostream>
#include <cstring>
using namespace std;
int num[15];
int dp[15][3];
int dfs(int pos,int flag,int s)//pos表示考虑当前位,s表示之前的状态,flag表示当前位是否可以任意
{
if(pos == -1) // 边界状态
return s==1||s==2;
if(flag && dp[pos][s] != -1) // 剪枝
return dp[pos][s];
int x = flag ? 9 : num[pos];
int ans=0;
for(int i=0;i<=x;i++) //递归找接下来的位
{
int k=s;
if(i==3)
k=1|s;
if(i==8)
k=2|s;
if(k<=2) // 剪枝
ans+=dfs(pos-1,flag||i<x,k); //dp[pos][s]表示当前考虑pos位,之前的状态为s,接下的(pos+1)个位的组合满足条件的个数
}
if(flag)
dp[pos][s]=ans;
return ans;
}
int cal(int x)
{
int k=0;
while(x)
{
num[k++]=x%10;
x/=10;
}
return dfs(k-1,0,0);
}
int main()
{
int t;
int l,r;
scanf("%d",&t);
memset(dp,-1,sizeof(dp));
while(t--)
{
scanf("%d%d",&l,&r);
printf("%d\n",cal(r)-cal(l-1));
}
return 0;
}
第二种写法是用循环写的,代码如下:
#include <cstdio>
#include <iostream>
#include <cstring>
using namespace std;
long long dp[15][3];//分成三种情况,dp[i][0]表示都没有,dp[i][1]表示只有3,dp[i][2]表示只有8
int num[15];
int cal(int x)
{
int l=1;
while(x)//分离数字存入num数组
{
num[l++]=x%10;
x/=10;
}
int flag3=0;
int flag8=0;//标记前位3和8出现否
long long ans=0;
for(int i=l-1;i>0;i--)
{
if(!flag3 && !flag8) //3和8都没出现过的时候
{
if(num[i]<=3)
{
ans+=num[i]*(dp[i-1][1]+dp[i-1][2]);//此情况只需考虑3和8单独存在的情况
if(num[i]==3)
flag3=1;
}
else
{
if(num[i]>3&&num[i]<=8)
{
ans+=(num[i]-1)*(dp[i-1][1]+dp[i-1][2]);//补其他数字的情况
ans+=dp[i-1][1]+dp[i-1][0];
if(num[i]==8)
flag8=1;
}
else
{
ans+=(num[i]-2)*(dp[i-1][1]+dp[i-1][2]);//补3或8的情况
ans+=dp[i-1][1]+dp[i-1][2]+dp[i-1][0]*2;
}
}
}
else
{
if(flag3&&!flag8)
{
if(num[i]<=8)
{
ans+=num[i]*(dp[i-1][1]+dp[i-1][0]);
if(num[i]==8)
flag8=1;
}
else
{
ans+=(num[i]-1)*(dp[i-1][1]+dp[i-1][0]);
}
}
else
{
if(!flag3&&flag8) //8已存在,不要考虑3的情况
{
if(num[i]<=3)
{
ans+=num[i]*(dp[i-1][2]+dp[i-1][0]);//少了补3的情况
if(num[i]==3)
flag3=1;
}
else
{
ans+=(num[i]-1)*(dp[i-1][2]+dp[i-1][0]);
}
}
}
}
}
return ans;
}
int main()
{
dp[0][0]=1;
dp[0][1]=dp[0][2]=0;
for(int i=1;i<15;i++)
{
dp[i][0] = 8* dp[i-1][0]; //在没有3和8的基础上,再补其他的8个数
dp[i][1] = dp[i-1][1] * 9 + dp[i-1][0];//在只有3的基础上补除8以外的数加上没有3和8的基础上补3
dp[i][2] = dp[i-1][2] * 9 + dp[i-1][0];//在只有8的基础上补除3以外的数加上没有3和8的基础上补8
}
int t;
scanf("%d",&t);
while(t--)
{
int l,r;
scanf("%d%d",&l,&r);
printf("%d\n",cal(r+1)-cal(l));
}
}