PAT甲級1049 Counting Ones (30分) ***感覺不簡單,使用遞歸

1049 Counting Ones (30分)
The task is simple: given any positive integer N, you are supposed to count the total number of 1’s in the decimal form of the integers from 1 to N. For example, given N being 12, there are five 1’s in 1, 10, 11, and 12.

Input Specification:
Each input file contains one test case which gives the positive N (≤2
​30
​​ ).

Output Specification:
For each test case, print the number of 1’s in one line.

Sample Input:
12
Sample Output:
5

最簡單直接循環,超時肯定的

最簡單的思路,從1到N,轉成string ,每一位判斷,但測試點肯定會超時

   long N;
    cin>>N;
    long sum=0;
    for(int i=1; i<=N; i++)
    {
        string str=to_string(i);
        for(int q=0; q<str.size(); q++)
        {
            if(str[q]=='1')
                sum++;
        }
    }
    cout<<sum<<endl;

考慮N位數字的所有1總和,寫一半沒思路了

然後我又想,通過排列組合,計算出所有N位的數字中,有幾個1,簡化一下,結果沒想到好辦法處理 10000到數字N之間的1的數目如何求
下面是部分代碼,看以後有好想法沒

//這個是實現排列,比如C52 =10
long cpi(int posNum,int i)
{
    long cpi=1;
    for(int q=0; q<i; q++)
    {
        cpi*=posNum-q;
    }

    for(int q=1; q<=i; q++)
    {
        cpi/=q;
    }
    return cpi;
}

long get(int posNum,int firstZero)
{
    if(posNum==1)
        return 1;
    if(firstZero==1)
    {
//        第一個數字可以爲0,那麼就是共有posNum個位置,
//        那麼1的數目從 1 到posnum個 ,現在假設有i個1
//        CposNum i  抽選出哪些位置放置1,其他位置posnum-i個 每個位置9個選擇除了1
        long sum=0;
        for(int i=1; i<=posNum; i++)
        {
            //C posNum i
            int get=cpi(posNum,i);
            sum+=cpi(posNum,i)*pow(9,posNum-i)*i;//關鍵是後面的乘以i
        }
        return sum;
    }
    else
    {
        long sum=0;
        sum+=get(posNum-1,1)*8;// 第一位是23456789的情況
        long sum2=0;
        for(int i=1; i<=posNum-1; i++)
        {
            //C posNum i
            sum2+=cpi(posNum-1,i)*pow(9,posNum-i-1)*(i+1);//關鍵是後面的乘以i
        }
        sum2+=pow(9,posNum-1);
        return sum+sum2;
    }
}

對位數進行遞歸

最後AC了的代碼是考慮遞歸,對數字n的每一位進行遞歸,知道最後一位就確定了一個數字,只要遞歸時候把之前位的1個數當做參數遞歸就好。最初想到這個思路,只是感覺可以用遞歸,只要我們適當的保存之前的數字1的個數,然後在數字n的最後一位遞歸的時候(其實也就確定了一個小於N的數字了),對含有1的個數,return 返回出去。
int df(int pos,int sum1) //pos是數字的那個位置,sum1是在pos之前的位置有幾個1
但是實現起來有個問題,就是比如數字n=789654

對於數字的第一個位置的數字7,我們能從i=0到7 循環一下,i=0 2 3 4 5 6 其實是一樣的,i=1是特殊的情況,遞歸時候需要sum1+1,i=7也是特殊情況,因爲我們不能讓數字超過N,這就限制了第二位的選擇,即第一位確定是7了第二位最高就是8,但是第二位0 2 3 4 5 6 其實是一樣的,第二位是1的情況進行第三位遞歸時候還是需要sum1+1,第二位是8了,又要限制第三位的選擇了,以此類推。

但是隻要我們再pos位置的數字小於N在pos位置的數字,在pos+1位置的遞歸就是無數字限制了,所以我就又寫了一個遞歸函數df2處理這種無數字限制的情況。

代碼放下面了,我感覺我下次也不一定能寫出這個代碼。。。。。

在這裏插入圖片描述

#include <bits/stdc++.h>
using namespace std;
string str;
int df2(int pos,int sum1)
{

    if(pos==str.size()-1)
    {
        int posNumber=str[pos]-'0';

        long sum=0;
        for(int i=0; i<=9; i++)
        {
            if(i==1)
                sum+=sum1+1;
            else
                sum+=sum1;
        }
        return sum;
    }

    int posNumber=str[pos]-'0';
    long sum=0;
    sum+=df2(pos+1,sum1+1);
    sum+=df2(pos+1,sum1)*9;
    //因爲已經確定現在的數字比N小了,數字的選擇也就無限制了

    return sum;

}

int df(int pos,int sum1)
{

    if(pos==str.size()-1)
    {
        int posNumber=str[pos]-'0';

        long sum=0;
        for(int i=0; i<=posNumber; i++)
        {
            //這裏其實就是判斷某個數字有幾個1 ,一直遞歸回去
            if(i==1)
                sum+=sum1+1;
            else
                sum+=sum1;
        }
        return sum;
    }

    int posNumber=str[pos]-'0';
    long sum=0;
    for(int i=0; i<=posNumber; i++)
    {
        
        //首先判斷是否是n在該位置的值,
        //比如n=789,i=7的時候就不能調用df2這個無限制的計算方法
        if(i==posNumber)
        {
            if(i==1)
                sum+=df(pos+1,sum1+1);
            else
                sum+=df(pos+1,sum1);
        }

        else
        {   //只要不是n在該位置的值,就是無限制的了,按照0-9情況考慮
            if(i==1)
                sum+=df2(pos+1,sum1+1);
            else
                sum+=df2(pos+1,sum1);
        }

    }
    return sum;

}


int main(void)
{
    long N=99;
    cin>>N;
    str=to_string(N);

    cout<<df(0,0);

}


純數學方法,參考柳神

https://blog.csdn.net/liuchuo/article/details/52264944

牛客方法也很多

https://www.nowcoder.com/questionTerminal/eb1e0fc83a25461e93b8bf2039e871b9?toCommentId=6269527

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章