HDU2089 不要62 数位dp

题目链接
题意:给你一个 n 和一个 m ,求在 [ n , m ] 之间的数有多少个不包含连续的 62 也不包含 4
题解:
这种题一看就是数位dp。 d p [ i ] [ j ] 表示前 i 位,当前这一位数字是 j 的方案数。状态转移方程是

d p [ i ] [ j ] = k = 0 9 d p [ i 1 ] [ k ]   (   ( j = 6 k = 2 )   j 4 )
初始状态为 d p [ 0 ] [ 0 ] = 1
我们把 [ 0 , 1 e 6 ] 的答案预处理出来,询问的时候就不用每次都计算了。
有两个细节:
第一个是传参的时候把两个数都加了一,因为统计答案时对于每一位我们是统计当前位小于传进来的参数的当前位的答案之和,所以统计到各位的时候答案会少了当前数,所以传参是答案要加一。
第二个是在统计答案是如果统计完当前位之后出现了有 4 或者连续 62 的情况,就直接退出了。原因是我们的dp[i][j]实际上是表示 j 10 i 1 ( j + 1 ) 10 i 1 1 之间的答案,举个例子,当要求的数的 241 的时候,当 i = 3 , j = 0 时,实际是累加上了 [ 0 , 99 ] 的答案,而由这个例子不难看出,当统计完 i = 2 , j = 3 ,也就是 [ 0 , 239 ] 之后,就已经是最后答案了,再继续加就是把不合法的往里累加了,也就是说,如果继续的话是会累加 [ 0 , 9 ] 的答案,但是前两位是 24 的情况下再往后加就是算 [ 24... , 24... ] 的答案了,显然它们都是不合法的,所以要停止累加。
代码:

#include <bits/stdc++.h>
using namespace std;

int n,m,dp[20][10],dig[50];
int work(int x)
{
    int ans=0,len=0;
    while(x)
    {
        ++len;
        dig[len]=x%10;
        x/=10;
    }
    dig[len+1]=0;
    for(int i=len;i>=1;--i)
    {
        for(int j=0;j<dig[i];++j)
        {
            if(dig[i+1]!=6||j!=2)
            ans+=dp[i][j];          
        }
        if (dig[i]==4||(dig[i+1]==6&&dig[i]==2))
        break;
    }       
    return ans;
}
int main()
{
    dp[0][0]=1;
    for(int i=1;i<=7;++i)
    {
        for(int j=0;j<=9;++j)
        {
            for(int k=0;k<=9;++k)
            {
                if(k==4||j==4||(j==6&&k==2))
                continue;
                dp[i][j]+=dp[i-1][k];
            }
        }
    }
    while(~scanf("%d%d",&n,&m))
    {
        if(n==0&&m==0)
        break;
        printf("%d\n",work(m+1)-work(n));
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/forever_shi/article/details/80904387
今日推荐