可以不用vector存每一位,直接计算某位的左边和右边的整数是多少。
可以不用讨论那么多细枝末节,只需要知道,当i为0时其左边整数不能为0,就够了。
# include <iostream>
# include <cmath>
using namespace std;
int dgt(int n) // 计算整数n有多少位
{
int res = 0;
while (n) ++ res, n /= 10;
return res;
}
int cnt(int n, int i) // 计算从1到n的整数中数字i出现多少次
{
int res = 0, d = dgt(n);
for (int j = 1; j <= d; ++ j) // 从右到左第j位上数字i出现多少次
{
// l和r是第j位左边和右边的整数 (视频中的abc和efg); dj是第j位的数字
int p = pow(10, j - 1), l = n / p / 10, r = n % p, dj = n / p % 10;
// 计算第j位左边的整数小于l (视频中xxx = 000 ~ abc - 1)的情况
if (i) res += l * p;
if (!i && l) res += (l - 1) * p; // 如果i = 0, 左边高位不能全为0(视频中xxx = 001 ~ abc - 1)
// 计算第j位左边的整数等于l (视频中xxx = abc)的情况
if ( (dj > i) && (i || l) ) res += p;
if ( (dj == i) && (i || l) ) res += r + 1;
}
return res;
}
int main()
{
int a, b;
while (cin >> a >> b , a)
{
if (a > b) swap(a, b);
for (int i = 0; i <= 9; ++ i) cout << cnt(b, i) - cnt(a - 1, i) << ' ';
cout << endl;
}
return 0;
}
# include <iostream> # include <cmath> using namespace std; int dgt(int n) // 计算整数n有多少位 { int res = 0; while (n) ++ res, n /= 10; return res; } int cnt(int n, int i) // 计算从1到n的整数中数字i出现多少次 { int res = 0, d = dgt(n); for (int j = 1; j <= d; ++ j) // 从右到左第j位上 数字i出现多少次 { // l和r是第j位左边和右边的整数 (视频中的abc和efg); dj是第j位的数字 int p = pow(10, j - 1), l = n / p / 10, r = n % p, dj = n / p % 10; // 计算第j位左边的整数小于l (视频中l = 000 ~ abc - 1)的情况 左边不等于abc的时候 说明都是比abc小的数字 if (i) res += l * p; //如果不是统计数字0 左边直接乘p就行了 n=ab3xxx p=1000 //n=1236055 6000-6999这里1000 第j位上的6出现了p次 但是左边还有16000-16999 26000-26999 36000-36999...1226000-1226999 共左边数字l(即123)个 所以是l*p else if (!i && l) res += (l - 1) * p; // 统计的数字i = 0, 左边高位不能全为0(视频中xxx = 001 ~ abc - 1) //少了0000-0999的一种情况 从10000-10999 开始 ... 1220000-1220999 13000-13999 共(l-1)次 // 计算第j位左边的整数等于l (视频中l = abc)的情况 只会和*j位后面的数*有关 //下面就是l的左边相等的情况 对第j位上 不会多算6000-6999 ...1226000-1226999里面的任意个集合 123开始的情况 if ( (dj > i) && (i || l) ) res += p;//第j位比现在统计的数字大 就可以直接加上p中情况 // n=1236055 则有1235000-1235999 999+1种情况 即p种 //当统计的数字i==0 且 l==0, 举例 n=123456 l==0 第j位为1 就是p=100000 此时000000-099999是不成立的 因为我要统计第j位为i的时候 有多少个这样的 数 而此时 000000-099999 显然和 100000-199999 第j-1位为2的时候重复了 if ( (dj == i) && (i || l) ) res += r + 1;//这是r有多少个 就是多少个+1 //if(dj==i) n=1236055 1236000-1236055 即55+1种情况 //当统计的数字i==0 且 l==0, 举例 n=123456 l==0且i==0 就是000000 -0123456 而这个时候显然和 第j-1的位的时候重复了100000-109999 //if(dj>i) n=1236000 则有1237000-1237999 所以是0 } return res; } int main() { int a, b; while (cin >> a >> b , a) { if (a > b) swap(a, b); for (int i = 0; i <= 9; ++ i) cout << cnt(b, i) - cnt(a - 1, i) << ' '; cout << endl; } return 0; }
%%%%%%%
感谢!好仔细!
%%%%%%%
感谢!!!看明白了
牛牛牛
xiexie!!!
谢谢谢谢谢谢谢谢!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
话说哪里有视频(
完善下函数的一些语句的解释:
# include <iostream> # include <cmath> using namespace std; int dgt(int n) // 计算整数n有多少位 { int res = 0; while (n) ++ res, n /= 10; return res; } int cnt(int n, int i) // 计算从1到n的整数中数字i出现多少次 { int res = 0, d = dgt(n); for (int j = 1; j <= d; j ++) // 从右到左第j位上数字i出现多少次,所有位上的次数加起来就是i出现的总次数 { // l和r是第j位左边和右边的整数 (视频中的abc和efg); dj是第j位的数字 int p = pow(10, j - 1), l = n / p / 10, r = n % p, dj = n / p % 10; // 计算第j位左边的整数小于l (视频中xxx = 000 ~ abc - 1)的情况 if (i) res += l * p; if (!i && l) res += (l - 1) * p; // 如果i = 0, 左边高位不能全为0(视频中xxx = 001 ~ abc - 1),并且&&l表示这时i也不能在最高位出现。 // 计算第j位左边的整数等于l (视频中xxx = abc)的情况 if ( (dj > i) && (i || l) ) res += p; //(i || l)表示i=0时,i不能出现在最高位(即l不能为0),因为这种数是不存在的 if ( (dj == i) && (i || l) ) res += r + 1; //(i || l)表示i=0时,i不能出现在最高位(即l不能为0),因为这种数是不存在的 } return res; } int main() { int a, b; while (cin >> a >> b , a) { if (a > b) swap(a, b); for (int i = 0; i <= 9; ++ i) cout << cnt(b, i) - cnt(a - 1, i) << ' '; cout << endl; } return 0; }
🌹🌹
谢谢你帮我渡劫
谢谢!!!
不过这题为啥属于DP呢
有点离谱
可惜你好像再也不会上线了 ,不知道以后还能不能看到你的题解
感谢这个题解!!
//代码缩短优化版(更好理解) #include<iostream> #include<cmath> using namespace std; int cnt(int num,int x){ int len=0,temp=num; //求数字的位数 while(temp){ len++; temp/=10; } int sum=0; for(int i=1;i<=len;i++){ //区别于y总的就在这一步,y总是用容器来装拆分后的各位上的数 //而这里是直接将数切割,就可以省去,将各位上的数合并的操作,并且在判断操作上 //也可以省去判断他是否是在最高位步骤,这里用l表示要找位的前面数大小,当在最高 //位时,l==0; int p=pow(10,i-1),l=num/p/10,r=num%p,di=num/p%10; //零不能在最高位 if(l||x){ //当要找的是零的时候,前面的数必定不可能是0(由于上面的判断,x和l只能一个是0) //只能从"001~abc-1"选数字了少了"000"这种情况,所以要(l-1) if(x==0)sum+=(l-1)*p; //当要找的不是零的时候(数字可以从000~abc-1) if(x!=0) sum+=l*p; //当前面xxx与abc相等时分两种情况 if( x==di) sum+=r+1; if( x<di ) sum+=p; } } return sum; } int main(){ int n,m; while(cin>>n>>m,n||m){ if(n>m){ swap(n,m); } int res=0; for(int i=0;i<=9;i++){ //前缀和 res=cnt(m,i)-cnt(n-1,i); cout<<res<<" "; } cout<<endl; } return 0; }
写的好 一下就看懂了
写得很简洁易懂,不错
特殊说明: 当第枚举第一位
j == 1
且i == 0
时, 很不巧res += -1 * p
了,即多减了一个p
, 按理来说这肯定是不对的, 情况数不应该被减p
巧就巧在 下面
d > i
是肯定成立的(最高位一定比 0 大), 这个时候又加上了p
但这个条件成立加上的
p
是最高位等于i
即0
时,右边可以取p
种情况, 但显然最高位不能是0
, 也就是不应该加上p
负负得正, 一加一减刚好抵消, 所以最终答案正确, 很巧
tql!
%%%
太牛了我焯,你是我大哥
# class版优化题解
#include <iostream> #include <cmath> #include <algorithm> using namespace std ; class count { public : int left ; int right ; int rad ; inline void read() { scanf("%d%d",&left,&right) ; if(left>right) swap(left,right) ; } inline int cnt(int n,int k) //从1~n出现了几次k { if(!n) return 0 ; int res=0 ; int len=int(log10(n)+1) ; for(int i=1;i<=len;i++) { int p=pow(rad,i-1) ; int front=n/p/rad ; int mid=n/p%rad ; int back=n%p ; if(k) res+=front*p ; else if(front) res+=(front-1)*p ; if((k||front)&&(mid>k)) res+=p ; else if((k||front)&&(mid==k)) res+=back+1 ; } return res ; } inline int query(int k) { return cnt(right,k)-cnt(left-1,k) ; } inline void print() { for(int i=0;i<rad;i++) printf("%d ",query(i)) ; puts("") ; } } ; int main() { struct count a ; a.rad=10 ; while(a.read(),a.left||a.right) a.print() ; return 0 ; }
感觉 cnt 里的写法不太好理解,整理了一个更容易理解的写法:
把 abcxefg ,分解成三种情况考虑,000xefg、(001~abc-1)xefg、abcxefg , 这样更清晰一点
#include<iostream> #include<cmath> using namespace std; int digit(int a) { int res = 0; while (a) a /= 10, ++res; return res; } int count(int a, int x) { int res = 0, d = digit(a); for (int i = 0; i < d-!x; ++i) { // x 如果是 0 少遍历一个 0bcdefg int p = pow(10, i); int cur = a / p % 10, high = a / p / 10; if (high) res += (high - 1) * p; //如果高位存在 001x000 ~ ab(c-1)x999 if (high && x) res += p; // 如果高位存在 且 x 非零 (1) 000x000 ~ 000x999 (2) 000x if (cur > x) res += p;// x 小于 d :abcx000~abcx999 if (cur == x) res += 1 + a % p; // x 等于 d : abcd000~abcdefg } return res; } int main() { int a, b; while (cin >> a >> b, a || b) { if (b > a) swap(a, b); for (int i=0; i<=9; ++i) cout << count(a, i) - count(b-1, i) << " "; puts(""); } return 0; }
可以从左往右计位吗?
超赞!
所以跟dp有关系吗
i || l 是什么意思
555555 555555这种是不对的
输出为· 0 1 0 0 0 5 0 0 0 0
答案应该是 0 0 0 0 0 6 0 0 0 0
Orz 非常清晰
这个i||l怎么理解啊
🐂🅱,帅