51Nod 1009 - 数字1的个数(计数 / 数位dp)

题目链接 http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1009

【题目描述】
给定一个十进制正整数N,写下从1开始,到N的所有正数,计算出其中出现所有1的个数。
例如:n = 12,包含了5个1。1,10,12共包含3个1,11包含2个1,总共5个1。

Input
输入N(1 <= N <= 10^9)

Output
输出包含1的个数

Sample Input
12

Sample Output
5

【思路】
两种思路,当成计数问题或者是数位dp,计数的话思路可能好理解一点,就是由低到高考虑每一位上出现的1的次数。就拿一个数的百位数字来说,通过不断尝试我们可以发现,如果输入的数n的百位上的数字为0,比如n=12045那么[1,n]中百位上出现的1的数字分别是{100~199,1100~1199,2100~2199,…12100~12199} 百位上一共是出现了12*100=1200个1,也就是只和更高位有关系,百位上1的个数=(n%1000)×(100);如果百位上的数字为1,比如n=12145,那么除了刚才列举的所有情况,还要再加上{12100~12145} 这当中百位上的146个1,(注意是146个,不是145个) 所以百位上1的个数=(n%1000)×(100)+(n-n%1000+1);如果说百位上的数字大于1的话,比如n=12245,那么除了最开始枚举出来的情况,还要再加上{12100~12199} 这当中的100个1,所以百位上1的个数=(n%1000)×(100)+100.上面所讲是拿百位为例,实际是要对每一位进行计算,循环遍历每一位即可,具体细节看代码.

#include<bits/stdc++.h>
using namespace std;

int main(){
    int n;
    while(scanf("%d",&n)==1){
        int tmp=10;
        int ans=0;
        while(1){
            int cur=n%tmp;//忽略高位后当前位的数 
            ans+=(n/tmp)*(tmp/10);
            if(cur>=(tmp/10)*2){//当前位大于1 
                ans+=tmp/10;
            }
            else if(cur>=tmp/10){//当前位等于1 
                ans+=cur-tmp/10+1;
            }
            if(tmp>n) break;
            tmp*=10;
        }
        printf("%d\n",ans); 
    }
    return 0;
}

数位DP,直接设 dp[pos][pre] 为枚举到pos位时,前一位是pre时的答案,dp[pos][0] 表示前一位不是1,dp[pos][pre] 表示前一位是1,记忆化搜索

#include<bits/stdc++.h>
using namespace std;

int pw[15]={1,10,100,1000,10000,100000,1000000,10000000,100000000,1000000000};
int a[20];
int dp[20][2];

int dfs(int pos,int pre,int limit){
    if(pos==-1) return pre==1?1:0;
    if(!limit && dp[pos][pre]!=-1) return dp[pos][pre];

    int up=limit?a[pos]:9;
    int ans=0;
    for(int i=0;i<=up;++i){
        if(pre==1){
            if(limit && i==up){
                int tmp=0;
                for(int j=pos-1;j>=0;--j){
                    tmp=tmp*10+a[j];
                }
                ans+=tmp+1;
            }
            else{
                ans+=pw[pos];
            }
            ans+=dfs(pos-1,i==1,limit && i==up);
        }
        else{
            ans+=dfs(pos-1,i==1,limit && i==up);
        }
    }
    if(!limit) dp[pos][pre]=ans;
    return ans;
}

int solve(int x){
    int pos=0;
    while(x){
        a[pos++]=x%10;
        x/=10;
    }
    return dfs(pos-1,-1,true);
}

int main(){
    int n;
    while(scanf("%d",&n)==1){
        memset(dp,-1,sizeof(dp));
        printf("%d\n",solve(n));
    }
    return 0;
}
posted @ 2018-03-10 20:57  不想吃WA的咸鱼  阅读(119)  评论(0编辑  收藏  举报