题意:我们定义十进制数x的权值为f(x) = a(n)*2^(n-1)+a(n-1)*2(n-2)+...a(2)*2+a(1)*1,a(i)表示十进制数x中第i位的数字。题目给出a,b,求出[0,b]有多少个不大于f(a)的数。
我理解的数位DP就是通过按位记忆化搜索找出所有满足条件的小于等于n的数。然后对于第i位,状态s是由其高位确定的。
数位DP的题目一般都很明显,关键在于状态的设计。看了一天的论文和模版,现在开窍了。
对于数位DP,状态通常比较容易写出,但是写出的状态可能有很多不合适,比如说爆数组。。。
那么要对状态进行压缩,或者重新确定状态(重新确定状态其实就是换一种方式搜索,要想好怎样更好的搜索)。
思路:开始设的dp[i][s][k] ,s为i的高位与2的幂的和且最后结果<=k 的数字个数。
显然是爆数组的。那么我设dp[i][s],s为目标-i的高位与2的幂,且最后结果>=0 的数字的个数。
我的代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
using namespace std;
int a,b,dp[12][20000],pow2[12];
int bit[12],top,target;
void init(){
memset(dp,-1,sizeof(dp));
pow2[0] = 1;
for(int i=1;i<12;i++) pow2[i] = 2*pow2[i-1];
//for(int i=0;i<12;i++) cout<<pow2[i]<<" ";cout<<endl;
}
void func(int n){
top = 0;target = 0;
for(;n;n/=10) bit[top++] = n%10;
for(int i=0;i<top;i++) target += bit[i]*pow2[i];
}
int dfs(int i,int s,bool e){
if(s < 0) return 0;
if(i == -1) return s >= 0 ? 1 : 0;
if(!e && dp[i][s] != -1) return dp[i][s];
int res = 0;
int d,u = e ? bit[i] : 9;
for(d = 0 ; d <= u ; d++){
res += dfs(i-1,s-pow2[i]*d,e&&(d==u));
}
return e ? res : dp[i][s] = res;
}
int solve(int n){
top = 0;
for(;n;n/=10) bit[top++] = n%10;
return dfs(top-1,target,1);
}
int main(){
init();
int cas;
scanf("%d",&cas);
for(int T=1;T<=cas;T++){
scanf("%d%d",&a,&b);func(a);
printf("Case #%d: %d\n",T,solve(b));
}
return 0;
}