PAT_A1049
PAT甲级1049.
目录
A1049
题目
样例
输入:
12
输出:
5
思路和坑点
寻找数字的规律,统计从1-n的数字中所有1的个数,不妨这样思考:对于每一位而言,该位为1的数字有多少个,然后把每一位取到的个数相加,即最终的答案。
对于数的每一位而言:(以1014为例)
如果该位等于0(比如百位),则其左侧必有数字,记为left(1),说明该位可以取到1,比如0124,但其左侧的数字left只能取到[0,left-1]共left种情况,考虑其右侧的数字可以取遍[0,该位权值-1]。因此,该位取1的数字有left*该位权值个。
如果该位等于1(比如十位),则其左边的数字记为left(10),右边数字记为right(4),左边数字可以先取[0,left-1],类似于等于0时的情况,但是由于该位等于1,当左边数字取到left时,右侧的数字只能取[0,right]。因此,该位取1的数字有left*该位权值+right+1个。
如果该位大于1,当该位左侧数字可以取[0,left],同时右侧数字可以取[0,该位权值-1]。因此,该位取1的数字有(left+1)*该位的权值个。
补充
为了能清楚的理解该原理,可以按照从低到高依次竖排打印出每一位取1的数字情况以便参考。AC代码后面补充了打印1014的个位,十位,百位,千位取1时候的数字情况的测试代码,可以运行一下,以作观察和理解。
AC代码
#include<bits/stdc++.h>
using namespace std;
int main(){
#ifdef ONLINE_JUDGE
#else
freopen("1.txt", "r", stdin);
#endif
int n;
cin>>n;
int ans=0;
int a=1; //a表示当前位的权值,初始化为1
while(n/a){ //依次循环取每一位
int left=n/(a*10); //取左边的数字
int right=n%a; //取右边的数字
int now=n/a%10; //去当前位
if(now==0) //当前位等于0时
ans+=left*a;
else if(now==1) //当前位等于1时
ans+=left*a+right+1;
else //当前位大于1时
ans+=(left+1)*a;
a*=10; //权值扩大,为下一位判定做准备
}
cout<<ans;
return 0;
}
用于理解的测试代码
#include<iostream>
#include<vector>
using namespace std;
int main(void){
int i=1;
vector<int> v1,v2,v3,v4;
for(int i=1;i<=1014;i++){
if(i%10==1) v1.push_back(i);
if(i/10%10==1) v2.push_back(i);
if(i/100%10==1) v3.push_back(i);
if(i/1000==1) v4.push_back(i);
}
int id1,id2,id3,id4;
id1=id2=id3=id4=0;
while(1){
if(id1<v1.size()) printf("%04d ",v1[id1++]);
else printf(" ");
if(id2<v2.size()) printf("%04d ",v2[id2++]);
else printf(" ");
if(id3<v3.size()) printf("%04d ",v3[id3++]);
else printf(" ");
if(id4<v4.size()) printf("%04d ",v4[id4++]);
else printf(" ");
putchar('\n');
if(id1==v1.size()&&id2==v2.size()&&id3==v3.size()&&id4==v4.size())
break;
}
}