HDU - 4734 F(x) (数位DP)
For a decimal number x with n digits (A nA n-1A n-2 ... A 2A 1), we define its weight as F(x) = A n * 2 n-1 + A n-1 * 2 n-2 + ... + A 2 * 2 + A 1 * 1. Now you are given two numbers A and B, please calculate how many numbers are there between 0 and B, inclusive, whose weight is no more than F(A).
InputThe first line has a number T (T <= 10000) , indicating the number of test cases.
For each test case, there are two numbers A and B (0 <= A,B < 10 9)OutputFor every case,you should output "Case #t: " at first, without quotes. The t is the case number starting from 1. Then output the answer.Sample Input
3 0 100 1 10 5 100Sample Output
Case #1: 1 Case #2: 2 Case #3: 13
分析:该题目数据的组数很多,且数的范围很大,能够想到使用数位DP来解决,但是需要注意DP的第二维应该用什么,容易想到的是,使用前面为止各个数位按照公式得到的值的总和,然后最后再比较这个值和F(A)
但是需要这样的话,会由于数据的组数过多,而我们每次需要重置DP数组,而超时,因为每组数据,DP的值只能单独使用(A的值不同导致的)
但是换一种角度的话,如果我们使用F(A)和当前值的差的话,这样DP就具有了对于所有的数据的泛用性,因为我们只需要考虑它减去剩下的数位得到的值,是否大于等于0
代码如下:
#include<cstdio> #include<iostream> #include<cstring> using namespace std; typedef long long LL; LL a[20]; LL dp[12][5050]; LL two[20]; LL cmp; LL x,r,t,f,Case=0,ini; LL dfs(int num,LL now,bool limit) //位数,传递条件 ,上界判断 { if(num==-1) return 1; //最后一位时,根据情况返回1或0 if(!limit && dp[num][now]!=-1) //已经走过此种状态 return dp[num][now]; LL ans=0; //计数 int up=limit?a[num]:9; //上界 for(int i=0;i<=up;i++){ if(i*two[num]>now)break; ans+=dfs(num-1,now-i*two[num],limit && i== up);//传递 } if(!limit) //判断是否可以储存 dp[num][now]=ans; return ans; } LL solve(LL x) //将x拆开存入a数组 { int num=0; while(x){ a[num]=x%10; //b表示进制!!! num++; x/=10; } /* for(int i=0;i<=num-1;i++) for(int j=0;j<=cmp;j++) dp[i][j]=-1;*/ return dfs(num-1,cmp,true);//传递 } int main() { two[0]=1; for(int i=1;i<=15;i++) two[i]=two[i-1]*2; memset(dp,-1,sizeof(dp)); scanf("%lld",&t); while(t--) { ini=0; f=1; cmp=0; Case++; scanf("%lld%lld",&x,&r); LL tmp=r; while(x>0) { cmp+=(x%10)*f; f*=2; x/=10; } printf("Case #%lld: %lld\n",Case,solve(r)); } return 0; }