51nod-1009-数位dp

题目链接:51nod1009


基准时间限制:1 秒 空间限制:131072 KB 分值: 5  难度:1级算法题
 收藏
 关注
给定一个十进制正整数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的个数
Input示例
12
Output示例
5

题目意思:给你一个数n求1-n中所有1的个数,,具体见题目描述


 题目思路: (第一次写数位dp,,之前看过别人怎么写,这次自己总算是写出来了,,,好菜啊) dp [i] [j] 表示从1到以j开头的 i 位数的1的个数 例如 dp [2][2] 就表示 1 - 29的1的个数  dp [5][2] 就表示1-29999 的1的个数 ,,,那么不难推出转移方程为 dp [i] [j] = dp [i] [j-1] + dp [i-1] [9] ,,,当j=1 时 dp [i] [j] 要加上 10^(i-1) 

 还有就是 dp [i]  [0]  = dp [i-1][9]+1; 这个方程可以自己去模拟模拟,还是不难得出的,,,最后求得时候就是按位来求,,把n拆分成 1位的数 + 2位的数+ ....+n的位数的数

然后就是把没位的答案加起来,,,例如  1234  拆分成 1000  + 200 + 30 +4  所以就等于dp [1][3] +dp [2] [2] +(dp [4] [0] +n-1000) (这里是开头为1的话就要加上后面位数的数)


AC代码:

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;

int dp[10][10];
void init()
{
    memset(dp,0,sizeof(dp));
    int num=1;
    for(int i=1; i<=9; i++)
    {
        dp[i][0]=dp[i-1][9]+1; //处理以0开头
        for(int j=1; j<=9; j++)
        {
            dp[i][j]=dp[i][j-1]+dp[i-1][9]; //转移方程
            if(j==1)
            {
                dp[i][j]+=num-1; //以1开头的数
            }
        }
        num*=10;
    }
}
int main()
{
    init();
    int n;cin>>n;
    int num=0; //记录位数
    int m=n;
    while(m)   //统计位数
    {
        num++;
        m/=10;
    }
    m=1;
    int ans=0;
    for(int i=1; i<num; i++)
        m*=10;
    while(num)
    {
        int x=n/m;
        if(x)
        {
            ans+=dp[num][x-1]; //每个位数的答案相加
            if(x==1)
            {
                ans+=(n-x*m); //以1开头的情况
            }
        }
        num--;
        n-=(m*x); //得到下一位数
        m/=10;
    }
    cout<<ans<<endl;
    return 0;
}







  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值