HDU 2089-不要62(dp?暴力?方法是真的多)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/I_believe_CWJ/article/details/79958531

不要62

Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 52666    Accepted Submission(s): 20065


Problem Description
杭州人称那些傻乎乎粘嗒嗒的人为62(音:laoer)。
杭州交通管理局经常会扩充一些的士车牌照,新近出来一个好消息,以后上牌照,不再含有不吉利的数字了,这样一来,就可以消除个别的士司机和乘客的心理障碍,更安全地服务大众。
不吉利的数字为所有含有4或62的号码。例如:
62315 73418 88914
都属于不吉利号码。但是,61152虽然含有6和2,但不是62连号,所以不属于不吉利数字之列。
你的任务是,对于每次给出的一个牌照区间号,推断出交管局今次又要实际上给多少辆新的士车上牌照了。
 

Input
输入的都是整数对n、m(0<n≤m<1000000),如果遇到都是0的整数对,则输入结束。
 

Output
对于每个整数对,输出一个不含有不吉利数字的统计个数,该数值占一行位置。
 

Sample Input
1 1000 0
 

Sample Output
80
 

Author
qianneng
 

Source


解题思路:本题给的数据范围是真的小,直接打表暴力即可水过,但本鶸鹡还是WA了好几发,忘了要离线解决问题,忘了前缀和为sum[b] - sum[a-1](而本鶸鹡写成了sum[b] - sum[a],真的,菜是原罪),最后还有一个更高深的解法,那就是数位dp,这个算法还是需要理解才能掌握的,接下来就直接上代码吧。


暴力1:数字打表(46ms)
#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<string.h>
#include<math.h>

using namespace std;

int s[1000010];

int main()
{
    int a, b, num[10], sum;
    sum = 0;
    s[0] = 1;
    for(int i = 1; i <= 1000000; i ++) {
        if(i%10 == 4 || i/10%10 == 4 || i/100%10 == 4 || i/1000%10 == 4 || i/10000%10 == 4 || i/100000%10 == 4) {
            s[i] = s[i-1];continue;
        }
        if(i%100 == 62 || i/10%100 == 62 || i/100%100 == 62 || i/1000%100 == 62 || i/10000%100 == 62) {
            s[i] = s[i-1];continue;
        }
        s[i] = s[i-1]+1;
    }
    while(~scanf("%d%d", &a, &b)) {
        if(a == 0 || b == 0) break;
        printf("%d\n", s[b] - s[a]);
    }
    return 0;
}

暴力二:字符串转换打表(93ms)
#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<string.h>
#include<math.h>
#include<stdlib.h>

using namespace std;

int sum[1000010], a, b;
char str[1000010];

int main()
{
    sum[0] = 1;
    for(int i = 1; i <= 1000000; i++) {
        itoa(i, str, 10); //把i转化为10进制的数再转化为字符串
        if(strstr(str, "4") == NULL && strstr(str, "62") == NULL) { //判断是否为子串
            sum[i] = sum[i-1] + 1;
            continue;
        }
        sum[i] = sum[i-1];
    }
    while(~scanf("%d%d", &a, &b)) {
        if(a == 0 || b == 0) break;
        printf("%d\n", sum[b] - sum[a-1]);
    }
    return 0;
}
我感觉 isoa函数 和 strstr函数 的使用非常巧妙!
isoa函数的介绍 (点击可查看,与sprintf函数作用差不多,但是有进制转化的功能)
strstr函数的介绍 (比较两个字符串是否有字串关系)

dp大法:数位dp法(0ms,在线解决,对于数据非常大的同样适用)
#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<string.h>
#include<math.h>
#include<stdlib.h>

using namespace std;

int num[10], a, b, k;
int dp[10][2];

int dfs(int len, bool is6, bool limet)
{
    if(len == 0)
        return 1;
    if(!limet && dp[len][is6]) {
        return dp[len][is6];
    }
    int cnt = 0;
    int maxx = (limet ? num[len] : 9);
    for(int i = 0; i <= maxx;  i++) {
        if((i == 2 && is6) || i == 4) //条件判断
            continue;
        if(i == 6)
            cnt += dfs(len - 1, true, limet && i == maxx);
        else cnt += dfs(len - 1, false, limet && i == maxx);
    }
    return limet ? cnt : dp[len][is6] = cnt;
}

int solve(int n)
{
    memset(num, 0, sizeof(num));
    memset(dp, 0, sizeof(dp));
    k = 0;
    while(n > 0) {
        num[++ k]  = n%10; //使用k ++时注意dfs中的k要-1,并且初始化为1
        n /= 10;
    }
    dfs(k, false, true);
}

int main()
{
    while(~scanf("%d %d", &a, &b)) {
        if(a == 0 || b == 0) break;
        printf("%d\n", solve(b) - solve(a-1));
    }
    return 0;
}


阅读更多
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页