计数问题数位统计dp

Posted joker D888

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了计数问题数位统计dp相关的知识,希望对你有一定的参考价值。

计数问题【数位统计dp】

题目

给定两个整数 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<1000000000

输入样例:

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

题目链接

思路

题目所求是[a,b]间所有数字上0~9出现的次数,等同于[1,b]上的次数减去[1,a-1]上的次数。

我们先将所给数字逐位分解,存入数组,便于后续取出。

设数字n被分解为 abcdefg (从高位到低位)。

主要解题思想就是枚举x(0~9)在每一位上出现的次数,现在举例其中的一部分,假设现在 求1在第4位上出现的次数

1 <= xxx1yyy <=abcdefg

(1) xxx = 000~abc-1 (abc个),则yyy=000~999(1000个),所以1出现的次数 abc*1000

(2) xxx = abc
(2.1) d < 1, abc1yyy > abcdefg ,违规,出现0次
(2.2) d = 1,yyy = 000~efg ,出现efg+1次
(2.3) d>1 , yyy = 000~999 ,出现1000次

对于 x = 0 要特殊处理下,因为0不能出现在首位,见代码中体现。

代码

#include<iostream>
using namespace std;
#include <vector>
// 等同于pow(10,n)
int power10(int n) 
	int ans = 1;
	while (n--)
		ans *= 10;
	
	return ans;

// 从num中[L,R]中的数组合取出
int get_num(const vector<int>& num, int L, int R) 
	int ans = 0;
	for (int i = L; i >= R; --i) 
		ans = ans * 10 + num[i];
	
	return ans;

// 返回1~num中x在其位上出现了几次
int cal(int x, int num) 
	if (num <= 0)
		return 0;
    // 逐位提出
	vector<int> bits;
	while (num) 
		bits.push_back(num % 10);
		num /= 10;
	
	int ans = 0;
	int n = bits.size() - 1;
	for (int i = n-!x; i >= 0; --i) 	// -!x 是因为0不能出现在首位
        // (1)
		if (i < n ) 	// 不是首位
			ans += get_num(bits, n, i+1) * power10(i);	
			if (!x)
				ans -= power10(i);	// 0不能在首位 所以x前的数字得从001开始,即把000开始的剔除
		
        // (2)
		if (x == bits[i])	// (2,2)
			ans += get_num(bits, i - 1, 0) + 1;
		else if (x < bits[i])	// (2,3)
			ans += power10(i);
	
	return ans;

int main()

	int a, b;
	while (cin >> a >> b && (a || b)) 
		for (int i = 0; i < 10; ++i) 
			if (a > b)	// 确保 b>a 
				swap(a, b);
			cout << cal(i,b) - cal(i,a - 1) << " ";
		
		cout << endl;
	
	return 0;

以上是关于计数问题数位统计dp的主要内容,如果未能解决你的问题,请参考以下文章

计数问题数位统计dp

计数问题数位统计dp

动态规划_计数类dp_数位统计dp_状态压缩dp_树形dp_记忆化搜索

[计数dp] 整数划分(模板题+计数dp+完全背包变种题)

动态规划区间计数数位统计状态压缩树形DP与记忆化搜索 题解与模板

ACM - 动态规划小白入门:背包 / 线性 / 区间 / 计数 / 数位统计 / 状压 / 树形 / 记忆化 DP