題目鏈接: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));
}
}