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

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

题目大意
给定一个区间[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 
#include 
#include 
#include 
#include 
#include 
#include 
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;j1;

    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<<" "<else cout<cout<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 
#include 
#include 
#include 
#include 
#include 
#include 
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;i10;}//对多余0的处理
   return ans;
}
int main()
{
  // freopen("input.txt","r",stdin);
   while(cin>>m>>n&&(m>=0)){
    if(m==0) cout<1<else cout<1)<return 0;
}

你可能感兴趣的:(组合数学)