HDU2089不要62

题目链接:HDU2089不要62
这道题目没有做出来,看来题刷的还不够。问题大致意思是:给出整数区间,求出在区间中的数不含4和62的个数。
一开始我觉得这道题目挺简单的,一来就暴力求解,结果超时,实在不知道怎么改,后来参考了大佬的初学数位DP–hdu 2089才对数位dp有所了解。

下面我们以[ 0 , 3548 ]这个区间来了解数位dp
这与数位区间有关,不能暴力求解,必须在数位上进行递推操作等。
要知道:两个数在进行比较时,我们是从两个数的高位到低位的顺序(如果顺序相反则不行)依次进行比较,当一个数a的高位数小于另一个数b的对应的高位数时,无论a的高位数后的数怎么取值,a始终是小于b的。例如:3548和32xx比较,百位5已经大于了百位2,因此,不管xx是什么,32xx都要小。
按照这个逻辑:我们在求[ 0 , 3548 ]这个区间满足条件的个数时,我们所求个数是等于区间[ 0 , 3000 ],[ 0 , 500 ],[ 0 , 40 ],[ 0 , 8 ]这四个区间所满足条件的个数的和
另外当我们算到计算到xx4x时,4后面不管取何值,这个数都不满足条件,因此可以直接跳过,以节省时间。对于上面的四个区间的满足条件的数的个数,我们就要用到数组dp[ i ][ j ],这个数组的含义是:以 j 开头的 i 位数满足条件的个数

for(int i=1;i<=7;i++) //n最大是7位数
	{
		for(int j=0;j<10;j++)//枚举第i位可能出现的数(10种)
		{
			for(int k=0;k<10;k++)//枚举第i-1位可能出现的数(也是10种)
			{
				if(j!=4&&!(j==6&&k==2))
					dp[i][j]  += dp[i-1][k];
			}
		}
	}

这段代码是对dp数组进行初始化,第[ i ]行(即 i 位数)的数除了2,4,6开头外,以其他数开头的 i 位数符合条件的数的个数都是 i - 1 位数所有数字开头的总和(体现了递归的思想)。
当有xx6xx的情况,则dp[ i ][ 6 ] = dp[ i - 1 ][ 0 ~ 9 ] - dp[ i - 1 ][ 6 ],若以4开头,这个数就不符合条件,那么dp[ i ][ 4 ]直接为0。

初始化后的dp数组:
在插入图片描述
现在既然能够计算出[ 0 , n ]符合条件的个数,通过[ 0 , n ] - [ 0 , m )来得出[ m , n ]的符合条件的个数。

附上大佬的AC代码:

#include <iostream>
#include <string>
#include <string.h>
#include <algorithm>
using namespace std;
int dp[10][10];
void init()
{
	memset(dp,0,sizeof(dp));
	dp[0][0] = 1;
	for(int i=1;i<=7;i++)
	{
		for(int j=0;j<10;j++)//枚举第i位可能出现的数
		{
			for(int k=0;k<10;k++)//枚举第i-1位可能出现的数
			{
				if(j!=4&&!(j==6&&k==2))//当遇到4时,直接退出,如前面的3548,若遇到62也是同理.
					dp[i][j]  += dp[i-1][k];
			}
		}
	}
}

int solve(int n)
{
	init();
	int digit[10];
	int len = 0;
	while(n>0)
	{
		digit[++len] = n%10;
		n/=10;
	}
	digit[len+1]=0;
	int ans = 0;
	for(int i=len;i;i--)
	{
		for(int j=0;j<digit[i];j++)
		{
			if(j!=4&&!(digit[i+1]==6&&j==2))
				ans+=dp[i][j];
		}
		if(digit[i]==4||(digit[i]==2&&digit[i+1]==6))
			break;
	}
	return  ans;
}
int main()
{
	int l,r;
	while(cin>>l>>r)
	{
		if(l+r==0)
			break;
		else
			cout<<solve(r+1)-solve(l)<<endl;
	}
	return 0;
}

最后,[ 0 , 3548 ]符合条件的个数 = 711 * 3 + 80 * 4 + 9 * 4 = 2489

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值