HDU 5787 (数位dp)(2016多校联赛)

5 篇文章 0 订阅

题目链接:

http://acm.hdu.edu.cn/showproblem.php?pid=5787


题目大意:

求l到r区间内,相邻k个数位两两互不相等的数的个数。


解题思路:

区间内统计符合某种数位条件的数的个数,很明显要用数位dp。由于k最大只有5,那么我们可以用当前位pos,当前位数的前四位preA,preB,preC,preD以及是否有相同的数来表示状态。

状态表示:dp[pos][preA][preB][preC][preD][same]  ,preA到D分别是当前位的前一位到前四位的数的取值。same若为0表示当前位pos前面都没有相同的数位,为1相反。preA到D的取值范围都在0~9。由于需要判断是否相等,所以赋初值的时候不能设0~9,为了节省数组空间,就赋初值为10。

目标状态:当前位为0且same==0的时候

注意事项:会出现前导0的情况,且由于是找互不相等的数的个数,所以前导0情况不可忽略。我们按照判断前导0惯用的套路,设置一个first来记录是否有前导0。为1则有,为0则无,然后把first的状态一起递归下去即可。


其余细节为数位dp惯用套路,详见:

http://blog.csdn.net/qq_28302731/article/details/52173767


代码如下:

#include <iostream>
#include <string.h>

using namespace std;

long long dp[20][11][11][11][11][2];
int a[20];

int k;

long long dfs(int pos,int preA,int preB,int preC,int preD,int limit,int first,int same)
{
    if(pos<1)
        return same==0;
    if(!first&&!limit && dp[pos][preA][preB][preC][preD][same]!=-1)
        return dp[pos][preA][preB][preC][preD][same];
    int End = limit? a[pos]:9;
    long long ans = 0;

    for(int i=0;i<=End;i++)
    {
        int samex=1;
        if(first)//若存在前导0
        {
            if(k==5)//依据k的值,与前面的k-1个数位判断,若都不相等,则当前位的same为0.
                if(i!=preA && i!=preB && i!=preC && i!=preD)
                    samex=0;
            if(k==4)
                if(i!=preA && i!=preB && i!=preC )
                    samex=0;
            if(k==3)
                if(i!=preA && i!=preB)
                    samex=0;
            if(k==2)
                if(i!=preA)
                    samex=0;

            if(i==0)//而且当前位还是0,那么前导0继续保留,first位依旧为1
                ans += dfs(pos-1,10,preA,preB,preC,limit&&(i==End),1,samex||same);//如果前面same已经为1了(不符合),那么即使samex为0,还是得用1递归下去

            else
                ans += dfs(pos-1,i,preA,preB,preC,limit&&(i==End),0,samex||same);
        }

        else
        {
                if(k==5)
                    if(i!=preA && i!=preB && i!=preC && i!=preD)
                        samex=0;
                if(k==4)
                    if(i!=preA && i!=preB && i!=preC )
                        samex=0;
                if(k==3)
                    if(i!=preA && i!=preB)
                        samex=0;
                if(k==2)
                    if(i!=preA)
                        samex=0;

                ans += dfs(pos-1,i,preA,preB,preC,limit&&(i==End),0,samex||same);
        }

    }
    if(!limit&&!first)
            dp[pos][preA][preB][preC][preD][same]=ans;

        return ans;
}

long long solve(long long n)
{
    int len=0;
    while(n!=0)
    {
        a[++len]=n%10;
        n /= 10;
    }
    long long ans=dfs(len,10,10,10,10,1,1,0);
    return ans;
}


int main()
{
   long long  l,r;
   while(cin>>l>>r>>k)
   {
       memset(dp,-1,sizeof(dp));

       cout<<solve(r)-solve(l-1)<<endl;
   }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值