HDU 2089 不要62 数位dp入门

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

dp[i][j]表示长度为i的数字中,开头的数字是j的时候,有多少种合法的情况。

预处理挺好理解,关键是那个统计,比较难。

比如我统计的是数字: 456

那么,从高到低枚举。先枚举第一位,4、再枚举5、....

那么,< 4 的,长度是3位的合法数字,都应该算做贡献。就是ans += dp[3][0...3]

dp[3][1]好理解,就是100、110、....199那些。

dp[3][2]那些同理,不处理到dp[3][4]也好理解,因为dp[3][4]包含了499那些,就是超越范围了。

然后,dp[3][0]是什么呢?其实就是0xx,其中,xx就是00---99

所以也就是所有的1位数和2位数的合法情况。(这个回归下dp预处理就知道了)

而后来的枚举下一位的5,dp[2][0...4]是同理的,但是其是和第一位的4结合的,也就是dp[2][0]包括了所有1位数的合法情况,然后组合成40x。

所以后面的枚举其实这是为了和上一位组合成合法情况。所以当其中某些数字破坏了条件,例如出现了4,或者已经出现了62,就要提前break

还有需要注意的是处理不到n这个数字的,因为都是枚举每一位的-1那个大小,所以只需要把他们 + 1即可。

 

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <assert.h>
#define IOS ios::sync_with_stdio(false)
using namespace std;
#define inf (0x3f3f3f3f)
typedef long long int LL;


#include <iostream>
#include <sstream>
#include <vector>
#include <set>
#include <map>
#include <queue>
#include <string>
#include <bitset>
int dp[10][20];
void init() {
    dp[0][0] = 1;
    for (int i = 1; i <= 7; ++i) {
        for (int j = 0; j <= 9; ++j) {
            if (j == 4) continue;
            for (int k = 0; k <= 9; ++k) {
                if (k == 4 || j == 6 && k == 2) continue;
                dp[i][j] += dp[i - 1][k];
            }
        }
    }
}
char str[222];
int calc(int val) {
    int lenstr = 0;
    while (val / 10 > 0) {
        str[++lenstr] = val % 10 + '0';
        val /= 10;
    }
    str[++lenstr] = val + '0';
    str[lenstr + 1] = '\0';
    int ans = 0;
    for (int i = lenstr; i >= 1; --i) {
        for (int j = 0; j <= str[i] - '0' - 1; ++j) {
            if (j == 2 && str[i + 1] == '6') continue;
//            if (j == 4) continue;
            ans += dp[i][j];
        }
        //后来枚举的,都是和前面的哪一位结合
        //比如456,就是4XX,然后45X
        if (str[i] == '4') break;
        if (str[i] == '2' && str[i + 1] == '6') break;
    }
//    cout << ans << endl;
    return ans;
}
int n, m;
void work() {
//    cout << dp[5][4] << endl;
    cout << calc(m + 1) - calc(n) << endl;
}
int main() {
#ifdef local
    freopen("data.txt", "r", stdin);
//    freopen("data.txt", "w", stdout);
#endif
    init();
    while (scanf("%d%d", &n, &m) != EOF && (n + m)) work();
    return 0;
}
View Code

 

复习了一下,下面是新的总结

HDU 2089 不要62
int dp[10][20]; // dp[i][j]表示长度是i的数字中,以j开头的合法情况
void init() {
    dp[0][0] = 1;
    for (int i = 1; i <= 7; ++i) { //枚举数字的长度是多少位
        for (int j = 0; j <= 9; ++j) { //枚举长度是i的开头数字
            if (j == 4) continue; //4是不合法情况,跳过了,所以dp[i][4] = 0;
            for (int k = 0; k <= 9; ++k) { //枚举长度是i - 1的开头数字
                if (k == 4 || j == 6 && k == 2) continue; //其实这个k = 4不跳过也一样,= 0
                dp[i][j] += dp[i - 1][k];
            }
        }
    }
    //dp[i][0] += dp[i - 1][0] + dp[i - 1][1] ... + dp[i - 1][9];
}
注意的是dp[i][0]的意义,dp[i][0]表示长度是i的,以0开头的合法数字。也就是0XX的形式,所以是包含了000---099,也就是所有合法的1位数和2位数之和。那000是什么?不管了,这个是多余的,也可以看作是合法的一位数,虽然题目的区间不包含000,但是R – L的时候会同时抵消这个多余的计数。
比如我统计的是数字: 456
那么,从高到低枚举。先枚举第一位,4、再枚举5、....那么,< 4 的,长度是3位的合法数字,都应该算做贡献。就是ans += dp[3][0...3],dp[3][1]好理解,就是100、110、....199那些。
dp[3][2]那些同理,不处理到dp[3][4]也好理解,因为dp[3][4]包含了499那些,就是超越范围了。而后来的枚举下一位的5,dp[2][0...4]是同理的,但是其是和第一位的4结合的,也就是dp[2][0]包括了所有1位数的合法情况,然后组合成40X。所以后面的枚举其实这是为了和上一位组合成合法情况。所以当其中某些数字破坏了条件,例如出现了4,或者已经出现了62,就要提前break。还有需要注意的是处理不到n这个数字的,因为都是枚举每一位的-1那个大小,所以只需要把他们 + 1即可。
int calc(int val) { //calc(R + 1) - calc(L + 1 - 1)
    int lenstr = 0;
    while (val / 10 > 0) {
        str[++lenstr] = val % 10 + '0';
        val /= 10;
    }
    str[++lenstr] = val + '0';
    str[lenstr + 1] = '\0';
    int ans = 0;
    for (int i = lenstr; i >= 1; --i) {
        for (int j = 0; j <= str[i] - '0' - 1; ++j) {
            if (j == 2 && str[i + 1] == '6') continue; //62X是不合法的,要跳过
            //那么j = 4呢?不跳过?其实是一样的,因为预处理的时候dp[i]][4] = 0的。
            ans += dp[i][j];
        }
        //后来枚举的,都是和前面的哪一位结合,比如456,就是4XX,然后45X
        if (str[i] == '4') break; //4XX,下一次枚举就不合法了
        if (str[i] == '2' && str[i + 1] == '6') break; 
    }
    return ans;
}
View Code

 

posted on 2017-01-18 20:30  stupid_one  阅读(148)  评论(0编辑  收藏  举报

导航