统计数字问题
2012-03-23 18:31
211 查看
问题描述:
一本书的页码从自然数1 开始顺序编码直到自然数n。书的页码按照通常的习惯编排,
每个页码都不含多余的前导数字0。例如,第6 页用数字6 表示,而不是06 或006 等。数
字计数问题要求对给定书的总页码n,计算出书的全部页码中分别用到多少次数字0,1,
2,…,9。
编程任务:
给定表示书的总页码的10 进制整数n (1≤n≤109) 。编程计算书的全部页码中分别用
到多少次数字0,1,2,…,9。
分析与解答:
由0,1,2……9组成的所有的n位数,从n个0到n个9共有10^n个n位数,其中全排列的情况下,每个数字使用的次数一样,设为f(n).
我们其实可以知道:f(n)=n10n-1
据此,可以从高向低位进行统计,再减去多余的0的个数即可。
本例程序,参考/article/4620134.html 但有多处开进。
上面程序中,
at() 函数 返回当前Vector指定位置loc的元素的引用. at() 函数 比 [] 运算符更加安全, 因为它不会让你去访问到Vector内越界的元素. 例如, 考虑下面的代码:
这段代码访问了vector末尾以后的元素,这将可能导致很危险的结果.以下的代码将更加安全:
取代试图访问内存里非法值的作法,at() 函数能够辨别出访问是否越界并在越界的时候抛出一个out_of_range异常.
程序网上还找到一种方法,虽然好理解,但是,不是很优化,算法复杂度不高,因为按照数逐个运算,随着数的增大。。。作为对比:
一本书的页码从自然数1 开始顺序编码直到自然数n。书的页码按照通常的习惯编排,
每个页码都不含多余的前导数字0。例如,第6 页用数字6 表示,而不是06 或006 等。数
字计数问题要求对给定书的总页码n,计算出书的全部页码中分别用到多少次数字0,1,
2,…,9。
编程任务:
给定表示书的总页码的10 进制整数n (1≤n≤109) 。编程计算书的全部页码中分别用
到多少次数字0,1,2,…,9。
分析与解答:
由0,1,2……9组成的所有的n位数,从n个0到n个9共有10^n个n位数,其中全排列的情况下,每个数字使用的次数一样,设为f(n).
我们其实可以知道:f(n)=n10n-1
据此,可以从高向低位进行统计,再减去多余的0的个数即可。
本例程序,参考/article/4620134.html 但有多处开进。
#include <iostream> #include <string> #include <sstream> #include <cmath> #include <vector> #include <fstream> using namespace std; double string_to_num(string str) {//字符串转换为double double back; istringstream instr(str); instr>>back; return back; } int main() { int len; double mm; //数字的个数 int countNum[10] = {0};//保存统计结果 vector<int> posNum; string pageNum; cout<<"input:"; cin>>pageNum; double num = string_to_num(pageNum); ////写入文件 //ofstream outfil("C:\\Users\\xxx\\Desktop\\input.txt"); //outfil<<num; len = pageNum.size(); if( len <= 1 ) { //如果只有个位数,直接放到数组输出 for(int i = 1; i <= int(num); i++) countNum[i] += 1; } else { const char* p = pageNum.c_str(); for(int i = 0; i < len; i++) { int curPosNum = int( *(p+i) ) - 48;//当前处理位置上的数字,从高位到低位 //书上的公式为全数组的形式,即000-999 //所以此公式适用于当前位为[0,curPosNum)时,其前面所有位的全形式。 //应该当前位数减一,由书上公式得f(n-1)=(n-1)pow(10,n-2) mm = pow(10.0,len-i-2) * (len-i-1); //当个位的时候,位数n=len-i=1,mm为0 if( mm != 0) {//判断是不是个位 //对低于当前位的位置先统计每个数的个数,当前位留到下一次 for(int j = 0; j < curPosNum; j++) //小于这个数的每个数的低位置的数的个数 { for(int k = 0; k < 10; k++) countNum[k] += mm; countNum[j] += pow(10.0,len-i-1); //加入当前位的个数 } if( !posNum.empty() ) { //判断是否为空 for(int m = 0; m < posNum.size(); m++) { //当不为空的时候,计算每个容器vector中前个元素(即高位)应该增加的 //个数(在当前数字为curPosNum时),不包括全为0的情况(因为只有一个 //全为0,不能加多次,留在后面处理) //核心:(出来一个低位,就把高位+相应个数) int tt = posNum.at(m); //安全防止溢出 countNum[tt] += ( pow(10.0,len-i-1)*curPosNum ); } } posNum.push_back(curPosNum); if( i == 0) //第0位 { //去掉多余的0 for(int j = 0; j < len; j++) countNum[0] -= pow(10.0,len-j-1) ; //我得方法,从高位开始 //如:0xx,0个数+10^2;再看0x,0个数+10^1;x,0个数+10^0 } } else {//指针位置来到个位上了,我的方法 for(int n=0;n<=curPosNum;n++) {//首先个位数上的数[0,curPosNum]每个数+1 countNum +=1; } if( !posNum.empty() ) { for(int m = 0; m < posNum.size(); m++) { //其次,再考虑个位时,每个前面的高位对应的数都要+相应的个位数值的个数 int tt = posNum.at(m); countNum[tt] += ( pow(10.0,len-i-1)*curPosNum )+1 ; //+1就是为了处理全0的情况 } } } } } //cout<<"output:"<<endl; //ofstream outfile("C:\\Users\\xxx\\Desktop\\output.txt"); //for (int i=0; i<10; i++) //{ // outfile<<countNum[i]<<endl; //} for(int i = 0; i < 10; i++) {//输出个数 cout<<countNum[i]<<endl; } return 0; }
上面程序中,
at函数
语法:TYPE at( size_type loc );
at() 函数 返回当前Vector指定位置loc的元素的引用. at() 函数 比 [] 运算符更加安全, 因为它不会让你去访问到Vector内越界的元素. 例如, 考虑下面的代码:
vector<int> v( 5, 1 ); for( int i = 0; i < 10; i++ ) { cout << "Element " << i << " is " << v[i] << endl; }
这段代码访问了vector末尾以后的元素,这将可能导致很危险的结果.以下的代码将更加安全:
vector<int> v( 5, 1 ); for( int i = 0; i < 10; i++ ) { cout << "Element " << i << " is " << v.at(i) << endl; }
取代试图访问内存里非法值的作法,at() 函数能够辨别出访问是否越界并在越界的时候抛出一个out_of_range异常.
程序网上还找到一种方法,虽然好理解,但是,不是很优化,算法复杂度不高,因为按照数逐个运算,随着数的增大。。。作为对比:
//这个题目有个最容易想到的n*log10(n)的算法。这是自己写的复杂度为O(n*log10(n))的代码: void statNumber(int n) { int i, t; int count[10] = {0}; for(i = 1; i <= n; i++) { t = i; while(t) { count[t%10]++; t/=10; } } for(i = 0; i < 10; i++) { printf("%d\n", count[i]); } }