/*
题目:完美数
题目链接:http://www.acdream.net/problem.php?id=1083
题目描述:在[L , R] 的正整数区间内,要么包含3 要么包含 8 的不同的整数有多少个?
解题思路:数位dp;
这题主要求出[0,n]区间内有多少个这样的数;
则[L,r]=[0,r]-[0,l-1];
先求出每位数以内有多少这样的数,比如:
dp[i][1]表示在i位数中只包含3不包含8的数的个数
dp[i][2]表示在i位数中只包含8不包含3的数的个数,
dp[i][3]表示在i位数中既不包含8也不包含3的数的个数。
注:i位数就是[0,10^i)以内的数;
这有状态方程转移为:
dp[i][1]=dp[i-1][3]+dp[i-1][1]*9; //表示这一位取3,前面所有的位既不含8也不含3,加上这一位不取8(剩下9个数),上一位数中只取3的数
dp[i][2]=dp[i-1][3]+dp[i-1][2]*9; //表示这一位取8,前面所有的位既不含8也不含3,加上这一位不取3(剩下9个数),上一位数中只取3的数
dp[i][3]=dp[i-1][3]*8; //表示这一位既不取8也不取3,上一位也既不取3,8的数,那么这一位能取的个数就是8;
我们求出某位数之内有多少个这样的数之后,接下来就是逐位分析了;
比如我们要求n以内的所有数,假设n=1386;我们先取n的最高位1,因为最高位是1,那么从0到999以内的数我们可以直接加上(dp[3][1]+dp[3][2]),
接下来的位是3,那么0-299之内的数我们可以加上3*(dp[2][1]+dp[2][2]),因为是3,我们标记一下3已经出现了,接下来一位是8,我们可以加上0-79
之内的数8*dp[1][1](因为已经出现了3,所以dp[1][2]就不能要了),这里还要注意一下,因为前面出现了3,那么我们还要加上8*dp[1][3]。又因为现在
出现了8,再往下就没意义了(8和3不能同时出现),最后答案就是上面的所有数的和。
如果不是因为3和8矛盾跳出循环的,还要考虑最后一位的数字;
注意:逐位分析的时候一定要充分考虑位的相关性,以下是我的代码;
*/
#include<stdio.h>
#include<string.h>
int dp[15][4];
void init()
{
int i;
memset(dp,0,sizeof(dp));
dp[0][3]=1;
dp[1][1]=1;
dp[1][2]=1;
dp[1][3]=8;
for(i=2;i<11;i++)
{
dp[i][1]=dp[i-1][3]+dp[i-1][1]*9;
dp[i][2]=dp[i-1][3]+dp[i-1][2]*9;
dp[i][3]=dp[i-1][3]*8;
}
}
int find(int n)
{
int digit[10],i,cnt,tag1=0,tag2=0;
if(n<3)return 0;
int sum=0,len=0;
while(n){digit[++len]=n%10;n/=10;}
for(i=len;i>0;i--)
{
if(tag1&&tag2)break;
cnt=0;
if(tag1||tag2)sum+=digit[i]*(dp[i-1][3]);
if(digit[i]>3)
{
cnt++;
if(!tag1) sum+=dp[i-1][2]+dp[i-1][3];
if(tag1||tag2) sum-=dp[i-1][3];
}
if(digit[i]>8){
cnt++;
if(!tag2) sum+=dp[i-1][3]+dp[i-1][1];
if(tag1||tag2) sum-=dp[i-1][3];
}
if(!tag2)sum+=(digit[i]-cnt)*(dp[i-1][1]);
if(!tag1)sum+=(digit[i]-cnt)*(dp[i-1][2]);
if(digit[i]==3)
tag2=1;
else if(digit[i]==8)
tag1=1;
}
if(tag1==1-tag2)sum++;
return sum;
}
int main()
{
int cas,i,l=0,r,sum=0;
init();
scanf("%d",&cas);
while(cas--)
{
scanf("%d%d",&l,&r);
if(l>r){i=l;l=r;r=i;}
r=find(r);
l=find(l-1);
printf("%d\n",r-l);
}
return 0;
}
/*
记忆化搜索版-(原理不变,代码更简洁)
(记忆化搜索版)数位dp资料详见博客:http://www.cppblog.com/Yuan/archive/2011/07/15/139299.html
*/
#include<stdio.h>
#include<string.h>
int dp[11][3],digit[11];
int dfs(int pos,int pre,int doing)
{
int i,now,ans=0;
if(pos==0) return pre>0;
if(!doing&&dp[pos][pre]!=-1)return dp[pos][pre];
now=doing?digit[pos]:9;
for(i=0;i<=now;i++)
{
if(i==3) ans+=(pre!=2)?dfs(pos-1,1,doing&&i==now):0;
else if(i==8) ans+=(pre!=1)?dfs(pos-1,2,doing&&i==now):0;
else ans+=dfs(pos-1,pre,doing&&i==now);
}
if(!doing) dp[pos][pre]=ans;
return ans;
}
int cal(int n)
{
if(n<3)return 0;
int len=0;
while(n){ digit[++len]=n%10;n/=10;}
return dfs(len,0,1);
}
int main()
{
memset(dp,-1,sizeof(dp));
int cas,l,r;
scanf("%d",&cas);
while(cas--)
{
scanf("%d%d",&l,&r);
printf("%d\n",cal(r)-cal(l-1));
}
return 0;
}