本题题目链接
数位dp知识点例题总结
题目大意:
给定a,b;求出f(a);判断[0,b]内,f(x)小于等于f(a)的数目
定位:数位dp;
比较:dp[pos][sum]
第一种:pos表示当前的位置,sum表示到了当前位置的数;
dp[pos][sum]表示pos位置,sum数据,达到满足要求的数的个数。pos==-1时,return sum>=fa(a);
第二种:pos当前位置 ,sum表示: f(a)-- 之前位置的值剩下的值;
dp[pos][sum]表示pos位置,用sum数据减去后面的数,结果大于等于0 的数的个数.pos==-1时,return sum >=0;
分析:
第一种:
题目是多组输入输出,而第一次使用后:dp[pos][sum]表示的是:dp[pos][sum]小于等于 第一组f(a) 的数
以后使用显然是不对的;如果每次都初始化dp,显然会超时。
第二种:
初始时的f(a)是不同的;
而确定了当前剩余位置的值,而后面的值又是确定的;
对于每一组测试数据来说,结果都是唯一的,这个dp方程就是可以通用的。
心得:
对于多组输入的数据,我们在外面对dp方程初始化,从而达到使用之前处理过的数据
!可是要思考一个问题:我们所定义的dp方程是针对一个问题的还是针对多个问题的
!如果是针对一个问题的,那么这个dp方程就不能单次初始化,甚至不可使用。
代码中关键部位有解析。
#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long LL;
int dp[10][37000];
int a[12];
int id[12];
int judge(int a)
{
int t[12];
int pos=0;
while(a)
{
t[pos++]=a%10;
a/=10;
}
int ans=0;
for(int i=0;i<pos;i++)
{
ans+=t[i]*id[i];
}
return ans;
}
int dfs(int pos,int sum,bool limit,bool lead)
{
if(sum<0) return 0;
if(pos==-1) return sum >= 0;
if(!limit&&!lead&&dp[pos][sum]!=-1) return dp[pos][sum];
int up=limit?a[pos]:9;
int ans=0;
for(int i=0;i<=up;i++)
{
ans+=dfs(pos-1,sum-i*id[pos],limit&&(i==up) ,lead&&(i==0));
}
if(!lead&&!limit) dp[pos][sum]=ans;
return ans;
}
int solve(int k,int u)
{
int mm=judge(u);
int pos=0;
memset(a,0,sizeof(a));
while(k)
{
a[pos++]=k%10;
k/=10;
}
int ans=dfs(pos-1,mm,1,1);
return ans;
}
int main()
{
id[0]=1;
for(int i=1;i<=11;i++)
{
id[i]=id[i-1]*2;
}
int t,u,v;
scanf("%d",&t);
memset(dp,-1,sizeof(dp));
for(int i=1;i<=t;i++)
{
scanf("%d%d",&u,&v);
printf("Case #%d: %d\n",i,solve(v,u));
}
return 0;
}