题解|数字游戏2

数字游戏

http://www.nowcoder.com/questionTerminal/d78446471cb341bf92a162b02d977819

数位dp

题意相信大家都懂(不懂就继续读吧学好文化课吧接下来看数据范围

1<=a,b<=231


显然,枚举是会T掉的,所以我们考虑数位dp(注意:我是用前缀和来处理出答案的,也就是最终答案为ansb-ansa-1)。


1.状态的设置

    首先,因为是数位dp,所以状态的第一位肯定是数位。接着,我们再来分析题目,看什么东西是必须记录的。然而,因为这一题的状态十分明显,就是各个数字之和,所以我们就直接拿这个和当作第二维的状态。最终的状态就是f[i][j],代表有i位,数字和为j且满足题目所求(数字和modN为0)的数字的个数有多少个


2.转移方程

    和基本的数位dp没有区别,我们也是采用记忆化搜索来进行状态转移。在搜索过程中,我们从0到尽可能的高位(为什么这么说?请见后面的3.a)中选择一个数。设选择的数为k,则对于每一个k,f[i][j]+=f[i+1][j+k]。状态转移方程也有了。但,接下来还有一些


3.需要注意的地方&优化

    a.选择数的时候,当前位能选的最大的数

       我们来看一个例子:设最多能选的数是2234。在某次搜索过程中,我们的当前状态是2???,那么此时次高位只能选0~2;但在另一次搜索过程中,当前状态为1???,这时的次高位却能选0~9。这种选择范围的差异是由于前一位选的数字导致的,所以我们设置一个bool,用来记录前一位是否与x的前一位相等,若不相等,则能选的数为0~9,相等就是0~x的当前位。

    b.状态的优化

       注意到,其实题目只是要求所选的数的数字和modN为0即可,所以我们可以将状态的第二维优化一下,改成j%N,这样就可以节省很多空间。


到此,这道题就被解决了。接下来就是代码了

#include<cstdio>
#include<cstring>
long a,b,n,ans,c[12],len,f[12][102];
long dfs(int pos,int rest,bool limit)
{
    if(pos>len)return rest==0;
    if(!limit&&f[pos][rest]!=-1)return f[pos][rest];
    long ret=0,res=limit?c[len-pos+1]:9;
    for(long i=0;i<=res;i++)ret+=dfs(pos+1,(rest+i)%n,limit&&i==res);
    return !limit?f[pos][rest]=ret:ret;
}
long part(long x)
{
    memset(f,-1,sizeof(f));
    len=0;
    while(x)c[++len]=x%10,x/=10;
    return dfs(1,0,1);
}
int main()
{
    while(scanf("%ld%ld%ld",&a,&b,&n)!=EOF)//多组数据
    {
        ans=part(b)-part(a-1);
        printf("%ld\n",ans);
    }
}


全部评论

相关推荐

走不到的路就这样算了吗:大佬硬气
点赞 评论 收藏
分享
点赞 评论 收藏
分享
评论
点赞
收藏
分享
牛客网
牛客企业服务