统计区间[a,b]各个数字出现的个数:poj 2282 The Counting Problem poj 3286

来源:互联网 发布:windows2008内存优化 编辑:程序博客网 时间:2024/05/20 00:17

组合数学中体现分析功底的基础题

题目大意
给定一个区间[a,b],统计区间内0,1,2,3,4,5,6,7,8,9各个数字出现的个数。

解题思路
问题转化为(0,n】区间各个数字出现的个数(最后用b减去a-1就是结果),关键是怎样去计数呢?
举个例子大家就会非常明白,以2345为例,分区间
————
0 0 0 1

1 0 0 0

2 0 0 0
————
2 0 0 1

2 3 0 0
————
2 3 0 1

2 3 4 0
————
2 3 4 1

2 3 4 5
————
第一个区间,先取得最高位2,高位上的0,1出现的次数为1000次,而2出现的次数是000~345,即346次;再看低位000~999,0~9出现的次数是相同的3*1000/10,高位0,1决定最终结果×2,即0~9次数加上2×3×1000/10;

注意这一步多考虑了前导零,而且只有在这一步,接下来的区间内并不会出现前导零,所以可以先不用管,等把所有区间都解决了,最后再减去前导零。

接下来分析第二个区间2001~2300.同样地,先取得最高位3(此时2的所有情况在第一个区间已经计数完毕,最高位已经失去意义),从001~300,高位上0,1,2出现的次数是100次,(0不是前导零啦!),低位00~99中0~9各出现3×2×100/10,分别计数;

以此类推至第3、4区间2301~2340,2341~2345,循环处理相同。

最后,不要忘记把多算的前导零减去,千位上的0多算了1000次,百位100,视为10,个位1,即最后减去1000+100+10+1

参考代码+部分解释

#include <iostream>#include <cstdio>#include <algorithm>#include <map>#include <vector>#include <cstring>#include <cmath>using namespace std;typedef long long ll;const int maxn = 7e4+10;int a,b,f[10]={0},g[10]={0};void swap(int &a,int &b){int temp=a;a=b;b=temp;return;}void solve(int n,int *h)//计算(0,n]区间0123456789出现的次数,记录在数组f[]中,以2345为例{   if(n==0) return;   int m=n,len=0;while(n){len++;n/=10;};n=m;      //得到长度len,用m恢复n   for(int i=len;i>0;i--){ //从高位向低位一个一个处理    int temp=(int)pow(10.0,i-1),digit=n/temp;     //取高位digit //处理高位    for(int j=0;j<digit;j++) h[j]+=temp;h[digit]+=n%temp+1;    int res=digit*(i-1)*temp/10;for(int i=0;i<10;i++) h[i]+=res;    n%=temp;  }   for(int i=len;i>0;i--) h[0]-=(int)pow(10.0,i-1);//对多余0的处理   return;}int main(){ //  freopen("input.txt","r",stdin);   while(cin>>a>>b&&a+b){     memset(f,0,sizeof(f));     memset(g,0,sizeof(g));     if(a>b) swap(a,b);     solve(a-1,f);     solve(b,g);     for(int i=0;i<10;i++)      if(i) cout<<" "<<g[i]-f[i];else cout<<g[i]-f[i];     cout<<endl;   }   return 0;}

poj 3286: How many 0’s?买一送一~

题目大意

统计区间[a,b]中数字0出现的个数

解题思路

是poj2282的一部分,解题思路是一样的,但是发现了poj2282算法中的一出问题(浮点数精度误差),强制转换double为int会有误差的,比如temp=100,实际会生成99,所以在这里我重新定义了一个变量x记录1000->100->10->1,避免了浮点数误差问题。

还要要注意的是开始区间m=0的问题,m-1是-1,求出来的结果与实际不符,所以我还是特判了一下m=0的情况,从1开始的仍然按照上一道题的处理方法解决。

参考代码

#include <iostream>#include <cstdio>#include <algorithm>#include <map>#include <vector>#include <cstring>#include <cmath>using namespace std;typedef long long ll;const int maxn = 7e4+10;ll m,n;ll solve(ll n)//计算(0,n]区间0出现的次数,记录在ans中{   ll ans=0;   ll m=n,len=0,x=1;while(n){len++;n/=10;x*=10;};n=m;      //得到长度len,用m恢复n   for(ll i=len;i>0;i--){ //从高位向低位一个一个处理    x/=10;int temp=x,digit=n/temp; //取高位digit //处理高位    if(digit>0) ans+=temp;else ans+=n%temp+1;    ll res=digit*(i-1)*temp/10;ans+=res;    n%=temp;  }   x=1;   for(int i=0;i<len;i++) {ans-=x;x*=10;}//对多余0的处理   return ans;}int main(){  // freopen("input.txt","r",stdin);   while(cin>>m>>n&&(m>=0)){    if(m==0) cout<<solve(n)+1<<endl;    else cout<<solve(n)-solve(m-1)<<endl;   }   return 0;}
1 0
原创粉丝点击