【POJ2282 The Counting Problem】求区间[a,b]中每个数字出现的次数

题目链接:http://poj.org/problem?id=2282

 

题目大意:给你两个数a,b,让你计算a到b之间所有的数总共包含多少0~9。

 

解题思路:  蛋疼的人生蛋疼的题。可以分别统计[0,b]中包含多少个0~9,[0,a]中包含多少个0~9,两个求得的结果相减就是答案了。

         举例分析: 299 百位数字有0,1,2,那么它们在百位时分别出现了100次,再对十位数和个位数的99分析,因为[0,99]中0~9出现的频率都是一样的,

分别为2(前一个数的值)*(位数)*10(固定一个数其余位能变化的次数)。

         所以对于4567,我们可以这样拆:0~3999,4000~4499,4500~4559,~4560~4567 。求得得总和就是答案了,还要注意一点的是前导0不能加进去,注意把加进去多余的前导0减掉。

View Code
 1 #include <iostream>
 2 #include <cstdio>
 3 #include <cstring>
 4 #include <sstream>
 5 #include <algorithm>
 6 using namespace std;
 7 
 8 void Cal(string s, int *num)
 9 {
10     int k=1;
11     for(int i=0; i<s.size()-1; i++)
12        k*=10, num[0]-=k;
13     for(int i=0; i<s.size(); i++,k/=10)
14     {
15         for(int j=0; j<s[i]-'0'; j++)
16             num[j]+=k;
17         for(int j=0; j<10; j++)
18             num[j]+=k/10*(s.size()-i-1)*(s[i]-'0'); ///!!这里要注意对于0~2999,[0,999]不仅要乘以位数还要乘以他前一位的值,指出现了多少次[0,999]
19         for(int j=0; j<i; j++)
20             num[s[j]-'0']+=(s[i]-'0')*k;  
21         num[s[i]-'0']++;  ///末尾都是0的情况
22     }
23 }
24 
25 int main()
26 {
27     int n, m;
28     string s;
29     int a[10], b[10];
30     while(~scanf("%d%d",&n,&m),n+m)
31     {
32         memset(a,0,sizeof(a));
33         memset(b,0,sizeof(b));
34         stringstream sa, sb;   ///中间过度函数,将int,double之类的类型转换成string
35         if(n>m) swap(n,m);
36         sa << n-1;
37         s=sa.str();  
38         Cal(s,a);
39         sb<<m;
40         s=sb.str();
41         Cal(s,b);
42         for(int i=0; i<10; i++)
43         {
44             if(i!=9) printf("%d ",b[i]-a[i]);
45             else printf("%d\n",b[i]-a[i]);
46         }
47     }
48     return 0;
49 }

 

补充解释

 1 #include <iostream>
 2 #include <sstream>
 3 #include <cstring>
 4 using  namespace std;
 5 
 6 ///以3456分析,3456可以划分为0~2999,3000~3399,3400~3449,3450~3456
 7 void Cal(string s, int* num)
 8 {
 9     int k=1;
10     for(int i=0; i<s.size(); i++)
11         k*=10, num[0]-=k;              ///去除去0作为前导的情况
12     for(int i=0; i<s.size(); i++, k/=10)
13     {
14         for(int j=0; j<s[i]-'0'; j++)  ///第i位时,对最高位进行分析,比如0~2999,最高位0和1都出现了1000次。
15             num[j]+=k;
16         for(int j=0; j<10; j++)  ///对0~2999分析,[0,999]中0~9出现的概率一样,最高位大小(2)*位数(3)*固定一位的最多次数(100)
17             num[j]+=(s[i]-'0')*(s.size()-i-1)*k/10;
18         for(int j=0; j<i; j++)    ///对于前面的每一位,还要后续处理后面没处理到的情况,比如处理最高位3时只处理了0~2999,而没有处理3000~3399,3400~3449,3450~3456等情况,后面处理的时候要考虑到
19             num[ s[j]-'0' ]+=(s[i]-'0')*k;
20         num[ s[i]-'0' ]++;  ///末尾都是0的情况
21     }
22 }
23 
24 int main()
25 {
26     int n;
27     int num[10];
28     while(cin >> n)
29     {
30         stringstream sa;
31         memset(num,0,sizeof(num));
32         string str;
33         sa << n;
34         sa >> str;
35         Cal(str,num);
36         for(int i=0; i<10; i++)
37             cout << num[i] <<endl;
38     }
39 }
View Code

 

转载于:https://www.cnblogs.com/kane0526/archive/2013/04/09/3011038.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值