现在的位置: 首页 > 综合 > 正文

HDU 2089 不要62(数位dp/暴力打表)

2018年04月28日 ⁄ 综合 ⁄ 共 1628字 ⁄ 字号 评论关闭

解题思路:

这题第一次做的时候,因为数据范围小,只有10^6,所以果断选择了暴力打表。在够表的时候,一开始少了a[i/10]==1这个条件,算出来的值总是多,,

然后加上后,扫一遍就过。

先看看暴力的代码吧.

# include<cstdio>
# include<iostream>
# include<cstring>

using namespace std;

# define MAX 1000000+4

int a[MAX];

void init()
{
    a[0] = 1;
    for ( int i = 1;i <= MAX;i++ )
    {
        if ( ( i%10!=4 )&&( i%100!=62 )&&a[i/10]==1 )
        {
            a[i] = 1;
        }
        else
        {
            a[i] = 0;
        }
    }
}

int main(void)
{
    int n,m;
    init();
    while ( cin>>n>>m )
    {
        int ans = 0;
        if (n==0&&m==0)
            break;
        for ( int i = n;i <= m;i++ )
        {
            if ( a[i]==1 )
            {
                ans++;
            }
        }

        cout<<ans<<endl;
    }

    return 0;
}

这几天刚好开始搞数位dp,所以,顺便把这道题用数位dp,重新写一下,感觉数位dp就是快。有关数位dp的学习,初学者我推荐从这个课件学起来,写的很好,,

点击打开链接

好了, 来分析下什么是数位dp了。

数位dp就是说,对于那种要求在一个区间上进行操作的问题的求解,但是我们无法用暴力,因为暴力会T,区间的跨度往往是很大的。那么我们就需要借助每一个数位之间的递推关系来得到最终的结果,而不是蛮力的搜索。。。

而在进行数位dp开始的时候,我们必须要进行预处理的工作,就是枚举0~9是每一位数的可能,比如说,让我们统计在[L,R]中满足题意的数的个数有几个的时候,我们就可以通过求解[0,R]和[0,L-1]这两个区间中满足题意的数字的个数,然后做差就好了。通过这个,我们就可以得到[0,n]的通用方法了。

对于一个小于n的数,肯定是从高位到低位出现某一位<n对应的那一位。

如 n = 58 n为十进制数。

   x = 49 此时x的十位<n的十位

   x = 51 此时x的个位<n的个位

有了上述性质,我们就可以从高到低枚举第一次<n对应位是哪一位。这样之前的位确定了,之后的位就不受n的限制即从00...0~99...9,可以先预处理,然后这时就可以直接统计答案。

代码:

# include<cstdio>
# include<iostream>
# include<cstring>

using namespace std;

int dp[8][10];//dp[i][j]表示的是以j开头的i位数中,不含4和62的个数
int digit[10];

void init()
{
    memset(dp,0,sizeof(dp));
    dp[0][0] = 1;
    for ( int i = 1;i <= 7;i++ )
    {
        for ( int j = 0;j <= 9;j++ )
        {//枚举第i位
            for ( int k = 0;k <= 9;k++ )
            {//枚举第i-1位
                if ( j!=4&&(!( j==6&&k==2 )))
                {
                    dp[i][j] = dp[i][j] + dp[i-1][k];
                }
            }
        }
    }
}


int cal ( int n )
{
    int len = 0;
    int ans = 0;
    while ( n!=0 )
    {
        digit[++len] = n%10;
        n = n / 10;
    }
    digit[len+1] = 0;

    for ( int i = len;i >=1 ;i-- )
    {
        for ( int j = 0;j < digit[i];j++ )
        {
            if ( j!=4&&(!(digit[i+1]==6&&j==2 )) )
            {
                ans+=dp[i][j];
            }
        }
        if ( digit[i]==4||( digit[i]==2&&digit[i+1]==6 ))
        {
            break;
        }
    }

    return ans;
}

int main(void)
{
    int n,m;
    while ( cin>>n>>m )
    {
        if ( n==0&&m==0 )
            break;
        init();

        printf("%d\n",cal(m+1)-cal(n));
    }

    return 0;
}

抱歉!评论已关闭.