数学总结之计数类问题(组合数学,数位DP)

本文介绍了如何统计【1,n】或【a,b】区间内具有特定性质的数的个数,涉及的计数问题包括单个数字出现次数、特定两位数出现次数及具有特殊性质的数。文章通过三种方法,即组合数学、数位动态规划(DP)和数位DP模板+DFS,详细解释了不同问题的解题思路,并提供了多个实例加以说明。
摘要由CSDN通过智能技术生成

【1,n】区间内或者【a,b】区间内的具有某种性质的数的个数如何统计?

此类问题往往分为以下几类:

1、统计单个数出现的次数,如:【10,1000】中数字0/1/2/3...出现的次数

2、统计某两位数出现的次数,如:【10,1000】中49出现的次数

3、统计具有某种特殊性质的数有多少个,如:【10,1000】中含有13并且能被13整除的数...

从第一类开始,由易到难

例1: 51 nod 1009

 

【法一】:直接计数(组合数学)

求【1,N】中1出现的个数规律如下:
如果当前位小于1,那么个数等于高位*当前位率
如果当前位等于1,那么个数等于高位*当前位率+低位+1
如果当前位大于1,那么个数等于(高位+1)*当前位率

 

#include<iostream>  
#include<cstdio>  
#include<cstdlib>  
#include<cstring>  
#include<algorithm>   
using namespace std; 
int getnum(int n,int i)
{  
    int res=0;  
    int factor=1;  
    while(n/factor) //当前位数 
	{  
         int high=(n/factor)/10; //高位 
         int curr=(n/factor)%10; //当前位 
         int low=n-(n/factor)*factor; //低位 
         if(curr<i)
             res=res+(high)*factor;      
		 else if(curr==i)
             res=res+(high)*factor+low+1;  
         else if(curr>i) 
             res=res+(high+1)*factor;  
         factor=factor*10;  
    }  
    return res;  
}  
   
int main()
{  
    int n;  
    while(~scanf("%d",&n))
	{  
          printf("%d\n",getnum(n,1));  
    }  
    return 0;
} 

【法二】:动态规划(数位DP)

 

设置状态:

dp数组表示【1,以j开头的i位数 】的区间内 1 出现的次数
如dp[1][1]表示以1开头的一位数,即【1,1】中1出现的次数
dp[2][2]表示以2开头的两位数,即【1,29】中1出现的次数
dp[2][3]表示以3开头的两位数,即【1,39】中1出现的次数

状态转移方程:

if(j!=1) dp[i][j]=dp[i][j-1]+dp[i-1][9];        
else dp[i][j]=2*dp[i-1][9]+date;

预处理dp数组:

如果不是1开头,则为j的上一个状态加i的上一个状态
如dp[2][3]即【1,39】=【1,29】+【1,9】
如dp[4][5]即【1,5999】=【1,4999】&#

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值