zoukankan      html  css  js  c++  java
  • AcWing 1081 度的数量

    题目传送门

    一、数位DP基础知识

    数位\(dp\)的题目一般会问,某个区间内,满足某种性质的数的个数

    对应的数位\(dp\)问题有相应的解题技巧

    • 利用前缀和,比如求区间\([x,y]\)中的个数,转化成求\([0,y]\)个数 -\([0,x-1]\)个数

    • 利用的结构来考虑(按位分类讨论

    何时能用 记忆化搜索 优化掉当前搜索分支
    当当前 数位 能够枚举集合内所有元素的时候(没有贴着上界),即 !op

    二、解题思路

    题目就是在一个区间\([x,y]\)内,找出有多少个符合题意的数,这里的符合题意是指:这个数的\(B\)进制表示中,其中有\(K\)个位置上是\(1\)、其他位上全是\(0\)

    比如样例中\(B=2\)\(K=2\),这个数是\(18\).就是把18化为二进制\(18\) 化为二进制数为:\(10010\),其中有\(2\)\(1\),其他位都是\(0\)

    再比如 \(B=3\)\(K=2\),这个数是\(12\).就是把\(12\)化为三进制\(12\)化为三进制为\(110\) ,其中也有\(2\)\(1\),其余都是\(0\).

    该类题目的核心就是根据数位进行分类讨论。

    第一步

    先将数\(x\)转化成为\(B\)进制,记为\(N\)

    //这是一段将n转化为B进制的代码
    //输入:n 和进制B
    #include<bits/stdc++.h>
    using namespace std;
    
    int main(){
    int n,B;
    cin >> n >> B;
    vector<int> a;
    while(n) a.push_back( n % B) , n /= B;
    for(int i = a.size()-1; i>= 0; i--) cout<< a[i]<<" ";
    }
    

    第二步

    数位DP,还是背模板来的快,提高课的数位DP讲解的东西太不好想了,也容易遗漏分枝,感觉\(dfs\)模板大法才是正解:

    #include <bits/stdc++.h>
    
    using namespace std;
    
    const int N = 32; //输入的数据范围2^31-1,也就是整数上界。2进制是最小的进制,32也够了
    
    int l, r, k, b;
    int a[N], al;
    int f[N][N];    //f[i][j] i:枚举到的位置 j:标记是1的个数
    
    /**
     *
     * @param pos 当前枚举到的数位
     * @param st  数字1出现了几次
     * @param op  是否贴上界
     * @return  在当前情况下,它“后面”符合条件的一共有多少个数
     */
    int dp(int pos, int st, int op) {
        //枚举到最后一位数位,是否恰有k个不同的1(也是递归的终止条件)
        if (!pos) return st == k;
        //记忆化搜索,前提是不贴着上界(可以枚举满这一位所有的数字)
        if (!op && ~f[pos][st]) return f[pos][st];
        //01数位dp,贴着上界时,本轮能枚举的最大数就是上界数位的数字和1之间的最小值
        int res = 0, up = op ? min(a[pos], 1) : 1;
        for (int i = 0; i <= up; i++) { //讨论每个后续可能,分拆的过程
            //剪枝,其实不剪枝也一样能过
            if (st + i > k) continue;   //出现的次数+ (0 或者 1) > k
            //累加后面所有的可能个数。合并的过程
            res += dp(pos - 1, st + i, op && i == a[pos]);
        }
        //模板,只记录不受上限限制的个数,受上限限制的每次重新计算
        return op ? res : f[pos][st] = res;
    }
    
    int calc(int x) {
        al = 0;
        memset(f, -1, sizeof f);        //模板的必要初始化步骤
        while (x) a[++al] = x % b, x /= b;          //把x按照进制分解到数组中,下标从1开始
        return dp(al, 0, 1);
    }
    
    int main() {
        cin >> l >> r >> k >> b;
        cout << calc(r) - calc(l - 1) << endl;
        return 0;
    }
    
  • 相关阅读:
    mybatis-day1
    java基础
    pytest进阶之html测试报告
    pycharm在github上clone项目
    selenium中的显示等待WebDriverWait与条件判断expected_conditions举例
    pytest-html报告中添加报错截图
    Fiddler抓包工具如何设置过滤域名
    os.system运行cmd命令时,命令中嵌套了引号
    Pytest之模块之间共享skipif标记
    Pycharm出现同一目录的py文件不能相互调用的问题
  • 原文地址:https://www.cnblogs.com/littlehb/p/15792444.html
Copyright © 2011-2022 走看看