#数位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;
}
原文地址:https://www.cnblogs.com/yuanyulin/p/14026705.html