zoukankan      html  css  js  c++  java
  • 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;        
    };  
  • 相关阅读:
    C#细说多线程(下)
    C#细说多线程(上)
    C#:进程、线程、应用程序域(AppDomain)与上下文分析
    C#委托与事件
    SQL Server 查询优化器运行方式
    SQL优化之索引分析
    C#反射机制
    Sql注入
    JAVA内存泄漏解决办法
    spring4声明式事务—02 xml配置方式
  • 原文地址:https://www.cnblogs.com/Lyush/p/2966817.html
Copyright © 2011-2022 走看看