题目链接:hdu 6148
数位DP比较简单的一题,然而我还是做了很久
别的没有什么,主要就是前导0是需要特殊考虑的一点。
若不考虑签到0,会导致010被排除,但其实是合法的。用lead=1来表示前导0的存在,在i==0&&lead==1时,下一步lead为1
其他就和正常的数位DP一样了
#include<iostream>
#include<cstdio>
#include<vector>
#include<set>
#include<map>
#include<string.h>
#include<cmath>
#include<algorithm>
#include<queue>
#include<stack>
#define LL long long
#define mod 1000000007
#define inf 0x3f3f3f3f
#define sqr(a) (a)*(a)
#define lan(a,b) memset(a,b,sizeof(a))
using namespace std;
int a[200];
LL dp[120][3][10];
int n;
LL pin(int pos,int st,int num,int lim,int lead)
{
//printf("%d %d %d\n",pos,st,num);
if(pos==-1)///递归基,如果所有数位都遍历完毕,判断条件是否满足并返回
{
return 1;
}
if(dp[pos][st][num]!=-1&&!lim&&!lead) return dp[pos][st][num];///记忆化搜索,如果之前搜过就直接返回,前提是lim=0
LL ans=0;
int up=lim?a[pos]:9;///确定上限
for(int i=0;i<=up;i++)
{
if(st==1&&i<num)
continue;
(ans+=pin(pos-1,(st==1||(lead==0&&i>num)),i,lim&&up==i,(lead==1&&i==0)))%=mod;
}
if(!lim&&!lead) dp[pos][st][num]=ans;///如果没有限制就更新记忆化数组
// printf("dp%d%d%d=%d\n",pos,st,num,ans);
return ans;
}
int main()
{
int t;
scanf("%d",&t);
lan(dp,-1);
while(t--)
{
char s[200];
scanf("%s",s);
n=strlen(s);
for(int i=0;i<n;i++)
a[i]=s[n-1-i]-'0';
n--;
LL ans=pin(n,0,0,1,1);
ans%=mod;
printf("%lld\n",ans-1);
}
return 0;
}