超短写法 + 注释 自我感觉应该比提高课的还要简洁点
看完一位大佬的超短题解 + 自己重新改了一点我认为的小错误后写的:
虽然本菜还未看过提高课的该题思路,但看了下犇犇的好多题解都cue到了提高课,弱弱地说一句:
我觉得这个思路比提高课的应该还是要简洁些 (如果不是就当我没说啊,狗头保命)
引入大佬的两句话 :
可以不用vector存每一位,直接计算某位的左边和右边的整数是多少。
可以不用讨论那么多细枝末节,只需要知道,当i为0时其左边整数不能为0,就够了。
#include <iostream>
#include <algorithm>
#include <cmath>
using namespace std;
int get(int n) { // 求数n的位数
int res = 0;
while (n) res ++, n /= 10;
return res;
}
int count(int n, int i) { // 求从1到数n中数i出现的次数
int res = 0, dgt = get(n);
for (int j = 1; j <= dgt; ++ j) {
/* p为当前遍历位次(第j位)的数大小 <10^(右边的数的位数)>, Ps:从左往右(从高位到低位)
l为第j位的左边的数,r为右边的数,dj为第j位上的数 */
int p = pow(10, dgt - j), l = n / p / 10, r = n % p, dj = n / p % 10;
// ps:下文的xxx、yyy均只为使读者眼熟,并不严格只是三位数啊~ 然后后续的...就代表省略的位数啦~
/* 求要选的数在i的左边的数小于l的情况:
(即视频中的xxx1yyy中的xxx的选法) --->
1)、当i不为0时 xxx : 0...0 ~ l - 1, 即 l * (右边的数的位数) == l * p 种选法
2)、当i为0时 由于不能有前导零 故xxx: 0....1 ~ l - 1,
即 (l-1) * (右边的数的位数) == (l-1) * p 种选法 */
if (i) res += l * p;
else res += (l - 1) * p;
/* 求要选的数在i的左边的数等于l的情况:(即视频中的xxx == l 时)
(即视频中的xxx1yyy中的yyy的选法)--->
1)、i > dj时 0种选法
2)、i == dj时 yyy : 0...0 ~ r 即 r + 1 种选法
3)、i < dj时 yyy : 0...0 ~ 9...9 即 10^(右边的数的位数) == p 种选法 */
if (i == dj) res += r + 1;
if (i < dj) res += p;
}
return res; // 返回结果
}
int main() {
int a, b;
while (cin >> a >> b, a) { // 输入处理,直到输入为0停止
if (a > b) swap(a, b); // 预处理-->让a为较小值,b为较大值
for (int i = 0; i <= 9; ++ i) cout << count(b, i) - count(a - 1, i) << ' ';
// 输出每一位数字(0 ~ 9)分别在[a,b]中出现的次数<利用前缀和思想:[l,r]的和=s[r] - s[l - 1]>
cout << endl; //换行
}
return 0; // 惯例:结束快乐~
}
有个解释上的 bug
当 i = 0, l = 0 时, 也就是考虑最高位为 0 时:
res += (l - 1) * p 做的是减 p , 实际上此时因为 l = 0 导致 1 ~ l ~ 1 不存在, 这种情况不成立, res 应该加 0.
if (i < dj) res += p 做的是加 p , 此时因为 l = 0 导致高位为 0(l), i 也为 0 的情况也不成立, res 应该加 0.
最终答案正确的原因是两个刚好相互抵消了.
从基础课爬来受教
谢谢啦~ 之前在视频下面的链接贴错了 后面有人评论我才发现然后删掉重新贴了一份
get可以直接返回to_string(n).size()
写的最适合基础课的题解Orz
%%%
真是小母牛坐飞机——牛逼上天了
是我目前看到最容易懂的了,orz
怎么感觉这样会重复计算呢
写的真的很好
写得太好了,点个赞
orz 大佬太牛了
tql orz
我愿意称呼 最强
我敲 这头像吓我一跳-_- hhh
你是y总我是谁?
通俗易懂!受教了!
客气啦, 有帮助就是好的~
nice,看了提高课的数位dp里y总给的通法,但还是打算先掌握这一种方法。因为我看剑指offer和leetcode上都有类似的简化版题目,提高课的那个数位dp解法写他们就有点冗长和小题大做了,对于找工党来说这解法再好不过啦哈哈,感谢感谢
大佬太厉害了,看了yls思考了很久都不太明白代码是怎么实现的,不过有一点不太理解就是 | cin >> a >> b, a这一段在只有a为0的时候不是也会停止循环嘛
是的,但是题目给的数据范围a,b>0呀
那不应该是
cin >> a >> b, a, b
这样才能保住在ab都大于0的时候为true嘛是的呀,但是题目的数据范围保证了如果是合法输入的话都会大于零哒~
sodayo
太牛了!站在了巨人的肩膀上,愚钝如我也能懂了Orz🤣
不敢当 蒻蒟一枚罢啦
if (i) res += l * p;
else res += (l - 1) * p; 大佬可以讲解一下这个吗
注释解释的很清楚了呀
正如楼主注释,和y总的例子
当i不为0时 L可以取000~999 一共是999+1种,一共1000种
当i为0时 由于不能有前导零 L就需要从001~999,一共999种
楼主注释很棒
悟了,大佬!!!!!!
orz,膜拜!!!