• #数位DP 计数问题


    数位DP

    一、计数问题

    题目链接
    第一次做真的很难,总之十分耗费时间。
    第一次批注得这么满……

    #include <iostream>
    #include <algorithm>
    #include <vector>
    using namespace std;
    const int N = 10;
    //get前面前缀部分的数值,即前面前缀总方案数
    int get(vector<int> num, int l, int r)
    {
        int res = 0;
        for (int i = l; i >= r; i -- ) res = res * 10 + num[i];
        return res;
    }
    
    //后缀有几位就是十的几次方
    int power10(int x)
    {
        int res = 1;
        while (x -- ) res *= 10;
        return res;
    }
    
    //这是1到n中i出现的次数
    int count(int n, int x){
    	//0的话就不要讨论了
        if (!n) return 0;
    	//将n的每一位都拆下来
        vector<int> num;
        while (n){
            num.push_back(n % 10);
            n /= 10;
        }
        n = num.size();
    	//接下来是统计在当前位置上取某个数会有多少种方案
        int res = 0;
    	/*
    		首先要明白,x是指在这一位取x,然后去求总共的方案数
    		x等于0的时候要另行讨论!
    		i从最高位开始枚举,枚举的是第i位出现了x的次数
    	*/
    	/*
    			这里假设这个序列式abcdefg,现在i在中间位置d,其前缀是abc, 后缀是efg
    			
    		一、这是第i位的前缀 小于 abc 的情况(当然,首先这个位置得有前缀才可以)
    			000~abc - 1, 999种(也可以说是 10的i-1的次方 - 1 种); 
    			
    		二、这是前缀等于abc的情况,记住!前提是前缀相等!
    			这里分别讨论第i位的三种情况,因为这三种情况对应了不同的三种后缀的情况!
    			1. 第i位 大于 目标最大值的第i位,那么后缀根本取不了,也就是0种情况。
    			2. 第i位 等于 目标最大的第i位
    				此时后缀可以随便取,于是有0~efg这些情况
    				num[i] == x, 0~efg
    			3. 第i位 小于 目标最大的第i位
    				此时后面的位置可以随便取,有1000种情况
    			    num[i] > x, 0~999(也就是0 ~ 10的i-1的次方 - 1 种 情况)
    	*/
        for (int i = n - 1 - !x; i >= 0; i -- ){
        	//如果i所在的不是最高位,那么就会有前缀,那么我们就来先处理前缀小于abc的情况
            if (i < n - 1){
            //res += 前缀的值 * 10的后缀的次方
            //这里前缀的情况应该是0~前缀(abc)-1,一共abc种情况,并没有把abc这种情况也算进去
                res += get(num, n - 1, i + 1) * power10(i);
                //如果x是0的话,前缀里面会少一种情况(就是这个情况:0...0,因为想要这一位取0,那么前缀不可能全是0);
                //比如三位数里想要第二位取0,那么就可以有102,103,106……但是绝对不会有002,003,006……等情况存在,不是吗?
                if (!x) res -= power10(i);
            }
            //然后我们来处理前缀就是abc的情况,当然啦,这里就不管有没有前缀了,都一样的~
    		//当前要取的数x 与 目标最大值的这一位数相等的时候 ,就是后面构成的数再加1(0这种情况)
            if (num[i] == x) res += get(num, i - 1, 0) + 1;
            //当前要取的数x 小于 目标最大值的这一位数 的时候,只要加上十的后面的数的位数的次方
            else if (num[i] > x) res += power10(i);
        }
    
        return res;
    }
    
    int main()
    {
        int a, b;
        while (cin >> a >> b , a){
       	 	//保证a小于b
            if (a > b) swap(a, b);
    		//分位置来讨论 每一位上 取 某一个数 会有多少种情况
            for (int i = 0; i <= 9; i ++ )
                cout << count(b, i) - count(a - 1, i) << ' ';
            cout << endl;
        }
    
        return 0;
    }
    
  • 相关阅读:
    无预挖无ICO-潜力币XDAG最强攻略
    DAG也许是真正的区块链3.0
    k-means+python︱scikit-learn中的KMeans聚类实现( + MiniBatchKMeans)
    使用sklearn进行K_Means聚类算法
    机器学习sklearn19.0聚类算法——Kmeans算法
    线性判别分析LDA原理总结
    LNMP V1.4一键快速部署Let's Encrypt免费SSL证书
    LNMP 1.x升级到LNMP 1.4教程及注意事项和多PHP版本使用教程
    Config Server高可用
    Config非对称加解密
  • 原文地址:https://www.cnblogs.com/yuanyulin/p/14026705.html
Copyright © 2020-2023  润新知