1049. Counting Ones (30)

给点一个数
计算1-n之间1的个数
如 11 有两个1

题目很短,思考过程却不是那么容易,刚开始开了个字符串慢慢算,只能得到22分

如下,不能得到全部,其他全部超时

#include <bits/stdc++.h>
using namespace std;
#define ll long long
int amax=9;
int k;
int cal(char s[]){
    int sum=0;
    for(int i=16;i>=0;i--){
        sum=sum*10+s[i]-'0';
    }
    return sum;
}
void add(char s[]){
    for(int i=0;i<=k;i++){
        if(s[i]<'9'){
            s[i]+=1;
            break;
        }
        else if(s[i]=='9'){
            s[i]='0';
        }
    }
}
int ji(char s[]){
    int cnt=0;
    for(int i=k;i>=0;i--){
        if(s[i]=='1') cnt++;
    }
    return cnt;
}
int main(int argc, char const *argv[])
{
    int N;
    cin>>N;
    char s[50];
    sprintf(s,"%d",N);
    k=strlen(s)+1;
    int cnt=0;
    char emm[50];
    for(int i=0;i<=49;i++){
        emm[i]='0';
    }
    while(1){
        int d=cal(emm);
        if(d==N) break;
        add(emm);
        cnt+=ji(emm);
    }
    printf("%d\n",cnt );
    return 0;
}

看了看大神的blog
自己理解一下
https://blog.csdn.net/tiantangrenjian/article/details/19908885

先看1位数的情况。

如果N = 3,那么从1到3的所有数字:1、2、3,只有个位数字上可能出现1,而且只出现1次,进一步可以发现如果N是个位数,如果N>=1,那么f(N)都等于1,如果N=0,则f(N)为0。
再看2位数的情况。

如果N=13,那么从1到13的所有数字:1、2、3、4、5、6、7、8、9、10、11、12、13,个位和十位的数字上都可能有1,我们可以将它们分开来考虑,个位出现1的次数有两次:1和11,十位出现1的次数有4次:10、11、12和13,所以f(N)=2+4=6。要注意的是11这个数字在十位和个位都出现了1,但是11恰好在个位为1和十位为1中被计算了两次,所以不用特殊处理,是对的。再考虑N=23的情况,它和N=13有点不同,十位出现1的次数为10次,从10到19,个位出现1的次数为1、11和21,所以f(N)=3+10=13。通过对两位数进行分析,我们发现,个位数出现1的次数不仅和个位数字有关,还和十位数有关:如果N的个位数大于等于1,则个位出现1的次数为十位数的数字加1;如果N的个位数为0,则个位出现1的次数等于十位数的数字。而十位数上出现1的次数不仅和十位数有关,还和个位数有关:如果十位数字等于1,则十位数上出现1的次数为个位数的数字加1;如果十位数大于1,则十位数上出现1的次数为10。
f(13) = 个位出现1的个数 + 十位出现1的个数 = 2 + 4 = 6;
f(23) = 个位出现1的个数 + 十位出现1的个数 = 3 + 10 = 13;
f(33) = 个位出现1的个数 + 十位出现1的个数 = 4 + 10 = 14;

f(93) = 个位出现1的个数 + 十位出现1的个数 = 10 + 10 = 20;
接着分析3位数。

如果N = 123:
个位出现1的个数为13:1, 11, 21, …, 91, 101, 111, 121
十位出现1的个数为20:10~19, 110~119
百位出现1的个数为24:100~123
f(123)= 个位出现1的个数 + 十位出现1的个数 + 百位出现1的次数 = 13 + 20 + 24 = 57;
同理我们可以再分析4位数、5位数。
根据上面的一些尝试,下面我们推导出一般情况下,从N得到f(N)的计算方法:
假设N=abcde,这里a、b、c、d、e分别是十进制数N的各个数位上的数字。如果要计算百位上出现1的次数,它将会受到三个因素的影响:百位上的数字,百位以下(低位)的数字,百位(更高位)以上的数字。
三种情况
如果百位上的数字为0,则可以知道,百位上可能出现1的次数由更高位决定,比如12 013,则可以知道百位出现1的情况可能是100~199,1 100~1 199,2 100~2 199,…,11 100~11 199,一共有1 200个。也就是由更高位数字(12)决定,并且等于更高位数字(12)×当前位数(100)。

如果百位上的数字为1,则可以知道,百位上可能出现1的次数不仅受更高位影响,还受低位影响,也就是由更高位和低位共同决定。例如对于12 113,受更高位影响,百位出现1的情况是100~199,1 100~1 199,2 100~2 199,…,11 100~11 199,一共1 200个,和上面第一种情况一样,等于更高位数字(12)×当前位数(100)。但是它还受低位影响,百位出现1的情况是12 100~12 113,一共114个,等于低位数字(113)+1。

如果百位上数字大于1(即为2~9),则百位上可能出现1的次数也仅由更高位决定,比如12 213,则百位出现1的可能性为:100~199,1 100~1 199,2 100~2 199,…,11 100~11 199,12 100~12 199,一共有1 300个,并且等于更高位数字+1(12+1)×当前位数(100)。

#include <bits/stdc++.h>
using namespace std;
int cal(int n){
    int cnt=0;
    int factor=1;
    int higher,lower,cur;
    while(n/factor){
        higher=n/(factor*10);
        lower=n-n/factor*factor;
        cur=(n/factor)%10;
        if(cur==0){
            cnt+=higher*factor;
        }
        else if(cur==1){
            cnt+=higher*factor+lower+1;
        }
        else{
            cnt+=higher*factor+factor;
        }factor*=10;
    }
    return cnt;
}
int main(){
    int n;
    cin>>n;
    printf("%d\n",cal(n));
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_38677814/article/details/80192110