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