AcDream 1083 完美数 数位DP

题意:询问一个区间内要么只包含3,要么只包含8的数有多少个?

解法:数位DP,可惜比赛的时候忘了怎么写了,随便写了个DP没有过,后来才调过了。正确解法是开设一个状态:
   f[i][0]表示剩下i+1位数中且暂时不含有3,8的满足要求的数的个数(i位数字可从全0取到全9,后同)
   f[i][1]表示剩下i+1位数中且暂时只含有3的满足要求的数的个数
   f[i][2]表示剩下i+1位数中且暂时只含有8的满足要求的数的个数
   f[i][3]表示剩下i+1位数中且已经含有3和8的满足要求的数的个数,该结果恒为零
   标程的解法使用了位运算来计算状态,非常清爽。

代码如下:

#include <cstdlib>

#include <cstdio>

#include <cstring>

#include <algorithm>

#include <iostream>

using namespace std;



int f[15][4], bit[15];



int new_s(int s, int i) {

    if (i == 3) return s | 1;

    if (i == 8) return s | 2;

    return s;

}



int dfs(int p, int s, int e) {

    if (p == -1) {

        return s == 1 || s == 2;

    }

    if (!e && ~f[p][s]) return f[p][s];

    int res = 0;

    int u = e ? bit[p] : 9;

    for (int i = 0; i <= u; ++i) {

        res += dfs(p-1, new_s(s, i), e&&i==u);

    }

    return e ? res : f[p][s] = res;

}



int cal(int x) {

    int idx = 0;

    while (x) {

        bit[idx++] = x % 10;

        x /= 10;    

    }

    return dfs(idx-1, 0, 1);

}



int main() {

    int T, l, r;

    memset(f, 0xff, sizeof (f));

    scanf("%d", &T);

    while (T--) {

        scanf("%d %d", &l, &r);

        printf("%d\n", cal(r) - cal(l-1));

        for (int i = 0; i < 10; ++i) {

            printf("%d ", f[i][3]);    

        }

        puts("");

    }

    return 0;    

}

 

自己写的搓代码,但是总归也过了:

View Code
#include <cstdlib>

#include <cstring>

#include <cstdio>

#include <iostream>

using namespace std;



int dp[15][10][3];

/*

    dp[i][j][0] 表示第i位值为j没有3和8的数有多少个

    dp[i][j][1] 表示第i位值为j只含有3的数有多少个

    dp[i][j][2] 表示第i位值为j只含有8的数有多少个

*/

int bit[15], idx;



int dfs(int pos, int num, int sta, int full) {

    if (!full && dp[pos][num][sta] != -1) {

        return dp[pos][num][sta];

    }

    if (pos == 0) {

        if (num == 3) return dp[pos][num][sta] = (sta==1);

        else if (num == 8) return dp[pos][num][sta] = (sta==2);

        else return dp[pos][num][sta] = (sta==0);

    }

    int end = full ? bit[pos-1] : 9;

    int temp = 0;

    for (int i = 0; i <= end; ++i) {

        if (sta == 0) {

            if (i == 3 || i == 8) continue;

            temp += dfs(pos-1, i, 0, full && i==end);

        } else if (sta == 1) {

            if (i == 8) continue;

            temp += dfs(pos-1, i, 1, full && i==end);

            if (num == 3 && i != 3) {

                temp += dfs(pos-1, i, 0, full && i==end);    

            }

        } else {

            if (i == 3) continue;

            temp += dfs(pos-1, i, 2, full && i==end);

            if (num == 8 && i != 8) {

                temp += dfs(pos-1, i, 0, full && i==end);

            }

        }

    }

    if (!full) dp[pos][num][sta] = temp;

    return temp;

}



int cal(int x) {

    int ret = 0;

    idx = 0;

    while (x) {

        bit[idx++] = x % 10;

        x /= 10;

    }

    for (int i = 0; i <= bit[idx-1]; ++i) {

        if (i != 8) {

            ret += dfs(idx-1, i, 1, i==bit[idx-1]); // 枚举最高位

        }

        if (i != 3) {

            ret += dfs(idx-1, i, 2, i==bit[idx-1]);

        }

    }

    return ret;

}



int main() {

    int T, a, b;

    memset(dp, 0xff, sizeof (dp));

    int x;

    scanf("%d", &T);

    while (T--) {

        scanf("%d %d", &a, &b);

        printf("%d\n", cal(b) - cal(a-1));

    }

    return 0;        

};  

 

你可能感兴趣的:(dp)