按照题意推dp就好
从最高位开始,根据是否受限往下递推记忆化保存结果
#include <cstdio>
#include <cmath>
#include <cstring>
#include <string>
#include <algorithm>
#include <queue>
#include <map>
#include <set>
#include <vector>
#include <iostream>
using namespace std;
typedef int ll;
const int maxn=5005+500;
int ans=0;
int dp[11][maxn];
int aa[11];
// pos = 当前处理的位置(一般从高位到低位)
// sum =限制不超过 F(a)
// limit = 是否受限,也即当前处理这位能否随便取值。如567,当前处理6这位,
// 如果前面取的是4,则当前这位可以取0-9。如果前面取的5,那么当前
// 这位就不能随便取,不然会超出这个数的范围,所以如果前面取5的
// 话此时的limit=1,也就是说当前只可以取0-6。
//
int F(int b)
{
int len=0;
while(b)
{
aa[++len]=b%10;
b/=10;
}
int sum=0;
for (int i=len; i>=1; i--)
{
sum+=(1<<(i-1))*aa[i];
}
return sum;
}
// 用DP数组保存这2个状态是因为往后转移的时候会遇到很多重复的情况。
//可根据需要增加维数,例如pre
int dfs(int pos,int sum,int flag)
{
int ans=0;
if (pos==0) //已结搜到尽头,返回"是否找到了答案"这个状态。
{
if (sum>=0) return 1;
else return 0;
}
if (sum<0) return 0;
//DP里保存的是完整的,也即不受限的答案,所以如果满足的话,可以直接返回。
if (!flag &&dp[pos][sum]!=-1) return dp[pos][sum];
int up;
if (flag) up=aa[pos];
else up=9;
//根据是否受限确定枚举的上界
for (int i=0; i<=up; i++)
{
int ff;
if (!flag) ff=0;
else
{
if (i==up) ff=1;
else ff=0;
}
ans +=dfs(pos-1,sum-i*(1<<(pos-1)),ff);
}
//DP里保存完整的、取到尽头的数据
if (!flag)
dp[pos][sum]=ans;
return ans;
}
int main()
{
memset(dp,-1,sizeof dp);
int tt,cas=0;
scanf("%d",&tt);
int m;
int cnt=1;
while (tt--)
{
int a,b;
scanf("%d%d",&a,&b);
int sum =F(a);
int len=0;
while(b)
{
aa[++len]=b%10;
b/=10;
}
ans=dfs(len,sum,1);
printf("Case #%d: %d\n",cnt++,ans);
}
}