#數位DP 計數問題

Yuan Yulin發表於2020-11-22

數位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;
}

相關文章