分析:数位DP的入门联系题,通过该題对数位DP有感觉了;dp[len][presum]代表长度为len权值不大于presum的数。记忆化搜索提高效率,除边界(因为边界上算出来的答案在其它数中可能是不完整的)外记录dp[len][presum]的值,下次发现以前计算过就直接return;dfs(len, presum, fg)表示求长度为len,不超过pre的全部符合条件的值。fg是控制边界的。
#include<iostream> using namespace std; int dp[11][20002]; int num[11]; int f(int x) //根据题目给的公式求出f(x) { int tmp,res; tmp=1; res=0; while(x) { res+=x%10*tmp; x/=10; tmp<<=1; } return res; } int dfs(int len,int presum,bool fg) //fg为边界标志 { int i,end,ans; if(len<0) return presum>=0;//如果数组num内的数用完了,就判断f(x)中剩余的是否还有,有就说明符合要求的有一个了 if(presum<0) return 0; //f(x)小于当前数,不符合要求,返回0 if(!fg && dp[len][presum]!=-1) return dp[len][presum]; //记忆化 end=fg?num[len]:9; //计算边界 ans=0; for(i=0;i<=end;i++) ans+=dfs(len-1,presum-i*(1<<len),fg&&i==end); if(!fg) dp[len][presum]=ans; //边界不能进行记忆化,因为对于另外的数来说可能计算的结果是不完整的 return ans; } int solve(int a,int b) { int len; len=0; while(b) //把b的每个位算出保存在num数组中 { num[len++]=b%10; b/=10; } return dfs(len-1,f(a),true); } int main() { int T,A,B,t; scanf("%d",&T); t=0; memset(dp,-1,sizeof(dp)); while(T--) { scanf("%d%d",&A,&B); printf("Case #%d: %d\n",++t,solve(A,B)); } return 0; }
原文地址:http://blog.csdn.net/a809146548/article/details/45700517