1. 程式人生 > 遊戲攻略 >《戰錘40K暗潮》碎顱者武器及技能介紹

《戰錘40K暗潮》碎顱者武器及技能介紹

題目連結:https://www.acwing.com/problem/content/340/

題目描述

給定兩個整數 a 和 b,求 a 和 b 之間的所有數字中 0∼9 的出現次數。
例如,a=1024,b=1032,則 a 和 b 之間共有 9 個數如下:
1024 1025 1026 1027 1028 1029 1030 1031 1032
其中 0 出現 10 次,1 出現 10 次,2 出現 7 次,3 出現 3 次等等…

輸入描述

輸入包含多組測試資料。
每組測試資料佔一行,包含兩個整數 a 和 b。
當讀入一行為 0 0 時,表示輸入終止,且該行不作處理。

輸出描述

每組資料輸出一個結果,每個結果佔一行。
每個結果包含十個用空格隔開的數字,第一個數字表示 0 出現的次數,第二個數字表示 1 出現的次數,以此類推。
0<a,b<100000000

示例

輸入

1 10
44 497
346 542
1199 1748
1496 1403
1004 503
1714 190
1317 854
1976 494
1001 1960
0 0

輸出

1 2 1 1 1 1 1 1 1 1
85 185 185 185 190 96 96 96 95 93
40 40 40 93 136 82 40 40 40 40
115 666 215 215 214 205 205 154 105 106
16 113 19 20 114 20 20 19 19 16
107 105 100 101 101 197 200 200 200 200
413 1133 503 503 503 502 502 417 402 412
196 512 186 104 87 93 97 97 142 196
398 1375 398 398 405 499 499 495 488 471
294 1256 296 296 296 296 287 286 286 247

分析

本題雖然頂著數位DP的標籤,但我目前實在是不明白跟DP啥關係,以後瞭解更多了再來更新。

問題轉換

我們要求給定x,y閉區間上所有數字上,0,1,2,···,9出現的次數。
我們可以將問題轉換,運用類似字首和的思想來思考。
count(i,j)表示在[1,i]這個閉區間上所有的數字中j出現的次數,如果我們可以求解這個問題,那我們就可以解決本題。

核心思路

對於這類問題,核心思路就是分類討論
對於給定的一個數 m=abcdefg 作為上界,以求解n(1 <= n <= 9)在第四位的出現次數為例進行分析。
第四位位y的數形如: xxxdyyy
當d!=0時:

  1. xxx -> [000,abc-1]
    yyy -> [000,999] 共abc*1000次
  2. xxx=abc
    d=n yyy -> [000,efg] 則共efg+1次
    d<n yyy -> [000,999] 則共1000次
    d>n 顯然共0次

當d=0時:

  1. xxx -> [001,abc-1]
    yyyy -> [000,999] 共(abc-1)*1000次
    因為 000 0 yyy 這個數實際上不存在第4位,所以000開頭不能被計入。
  2. xxx=abc
    d=n yyy -> [000,efg] 則共efg+1次
    d<n yyy -> [000,999] 則共1000次

以上就是對d位於中間數的情況的討論,按照同樣的方式就可以對d在最高位或最低位的情況進行討論,從而高效解決計數。

AC程式碼


#include <iostream>
#include <algorithm>
#include <vector>
#include <cmath>
#include <cstring>

using namespace std;

int get(vector<int> num, int l, int r)
{
	int res = 0;
	for (int i = r; i >= l; i--)
	{
		res = res * 10 + num[i];
	}
	return res;
}

int count(int x, int n)
{
	//如果傳入的x等於0 返回的res為0
	vector<int> num;
	while (x)
	{
		num.push_back(x % 10);
		x /= 10;
	}
	int res = 0;
	for (int i = 0; i < num.size(); i++)
	{
		if (i == 0) //對最低位的處理
		{
			res += get(num, i + 1, num.size() - 1);
			if (n == 0)
				res--;
			if (n <= num[i])
				res++;
			continue;
		}
		if (i == num.size() - 1) //對最高位的處理
		{
			if (n == 0) //最高位是0直接pass
				continue;
			if (n < num[i])
				res += (int)pow(10, num.size() - 1);
			if (n == num[i])
				res += get(num, 0, i - 1) + 1;
			continue;
		}

		//對中間位的處理
		res += get(num, i + 1, num.size() - 1) * (int)pow(10, i);
		if (n == 0)
			res -= (int)pow(10, i);
		if (n < num[i])
			res += (int)pow(10, i);
		if (n == num[i])
			res += get(num, 0, i - 1) + 1;
	}
	return res;
}

int main()
{
	int a, b;
	while (cin >> a >> b, a || b)
	{
		if (a > b)
			swap(a, b);
		for (int i = 0; i < 10; i++)
		{
			if (i)
				cout << " " << count(b, i) - count(a - 1, i);
			else
				cout << count(b, i) - count(a - 1, i);
		}
		cout << endl;
	}
	return 0;
}