|
发表于 2008-5-22 21:50:28
|
显示全部楼层
嗯, 很好----发现大部分人理解错了题, 你是少有的理解了的人.
Hand
Post by Mason_hou;1818060
今天看了一下大家的思路,好像方向不对阿,要提高这算法的效率,关键在于搜索的步长,而不是讨论怎么计算1的个数,大家在这里都只是想方设法提高计算1个数的效率而已。计算1个数的公式pointer 已经得出,这已经是最高效率的了。而且也证明了n的范围,这点很重要(虽然证明过程有些笔误)。
不过还有最关键的问题是搜索的步长,在这点上pointer的算法并非最好,不过也比普遍的递进好太多了。我也试着做了一些改进:
一、更改一下n的下界,
首先可以证明除1外最小n>5:
由pointer的公式有
f(an,an-1.....a0)=(an-1...a0)+1+f(10^n-1) + f(an-1,...,0) , an=1
又
f(an,an-1.....a0) = (an...a0)
得
(an...a0)- (an-1...a0) = 1+f(10^n-1) + f(an-1,...,0)
-->
10^n = 1+n*10^(n-1) + f(an-1,...,0) <= 2( 1+ n*10^(n-1)) ( 因 f(an-1,...,0)<=f(an,...,0), 且 (an-1...a0)可以为 0)
于是有 n> 5,编程可以直接跳到 n=1e5进行搜索。
二、提高搜索步长:
因假设一个数n长为 L,d = n- f(n) 。
这里仅仅讨论d>0的情况。如果n增加能使d减少,则n至少固定含有一个'1'。并且在仅有一个1不变的情况下n每增加a,0<d-a<=d。基于这一思想,对于可能出现 L 个 '1',pointer的算法保守得使用 d/L 作为增加的步长,但实际上可以动态设置。比较简单得就是原有的'1'的个数加上 d 的长度作为除数即可。
三、连续打印连续的10个数:
可以从公式证明在如果 n 是所求,且除个,十位仅有1个'1',则连续的个十位由01-10都是所求。
程序:
- #include<stdio.h>
- #define __USE_ISOC99
- #include<stdlib.h>
- #define MAXNL 10 // It has been proofed that n<=8 and n_max = 1111111110
- #define MAN_N 1111111111
- long long tenpowers[MAXNL]= {1};// 10^i
- long long rempowers[MAXNL]= {0};// remainder power: i*10^(i-1)
- int remainders[MAXNL]={0}; // remainders
- // Suppose that n= a* 10^i+b, where i >0 and b<10^i.
- // Then b_i = n- a*10^i, and b_num stores b_i.
- long long b_num[MAXNL]={0};
- int len=0; // length of n
- long long countOnes(long long n)
- {
- int i, j;
- long long ones_num=0;
- lldiv_t qr;
- // initial
- len =0;
- qr.quot=n;
- // count len and set b_num
- while(qr.quot) {
- qr=lldiv(qr.quot, 10L);
- remainders[len]=qr.rem;
- if (len>0) { // set b_num
- b_num[len]=b_num[len-1]+qr.rem*tenpowers[len];
- }
- else{
- b_num[len]=qr.rem;
- }
- len++;
- }
- // count ones
- for(i=len-1; i>0; --i) {
- if(remainders[i]>1) {
- ones_num+=tenpowers[i]+remainders[i]*rempowers[i];
- }
- else if(remainders[i]==1){
- ones_num+=rempowers[i]+b_num[i-1]+1;
- }
- }
- ones_num+=(remainders[i]>0)?1:0;
- return ones_num;
- }
- // count the number of one in remainders
- int numOfOne() {
- int count=0,i;
- for (i=1;i<len;i++) { // ignore the first remainder
- if ( 1 == remainders[i] ) {
- count++;
- }
- }
- return count;
- }
- // Print the adjoining ten number, each of who has only one '1' except the first one .
- int printAdjTen( long long n, int n_num) {
- int i;
- if (numOfOne() ==1) {
- for (i=0; i<9; i++) {
- printf("%4d: %lld\n", n_num++ ,++n);
- }
- return 1;
- }
- return 0;
- }
- // Get the length of one number
- int getLen(long long n) {
- int len =0 ;
- for (len=0;len<MAXNL;len++) {
- if ( n < tenpowers[len]) {
- break;
- }
- }
- return len;
- }
- int main(int argc, char * argv[])
- {
- long long ones_num, diff; // numbers of '1' ; difference between n and ones_num
- int i,n_num=1;
- int diff_len,step;
- long long n=1;
- for(i=1;i<MAXNL;++i) {
- tenpowers[i]=tenpowers[i-1]*10L;
- rempowers[i]=tenpowers[i-1]*i;
- }
- printf("%4d: %lld\n", n_num++ ,n); // print '1'
- n = 1e5; // It has been proved that next n must be larger than 1e5
- while(n<MAN_N) { // searching
- ones_num=countOnes(n);
- diff=n-ones_num;
- if(diff>len) {
- // set search step.
- step=numOfOne();
- if (step==0) {
- step = len-1;
- }else {
- diff_len = getLen(diff); // get length of diff
- step = step+diff_len;
- if (step==len) {
- step--;
- }
- }
- n+=diff/step+1;
- }else if(diff<0) {
- n+=(-diff);
- }else {
- if(diff==0){
- printf("%4d: %lld\n", n_num++ ,n);
- if ( printAdjTen( n, n_num) ) {
- n+=9;
- n_num+=9;
- }
- }
- ++n;
- }
- }
- return 0;
- }
复制代码 |
|