HDU-3555 Bomb(数位dp)

今天我来分享一下我博客中的第一道数位dp题。以Bomb这道题为例我将对数位dp做一个系统的分析。

The counter-terrorists found a time bomb in the dust. But this time the terrorists improve on the time bomb. The number sequence of the time bomb counts from 1 to N. If the current number sequence includes the sub-sequence "49", the power of the blast would add one point.
Now the counter-terrorist knows the number N. They want to know the final points of the power. Can you help them?

Input

The first line of input consists of an integer T (1 <= T <= 10000), indicating the number of test cases. For each test case, there will be an integer N (1 <= N <= 2^63-1) as the description.

The input terminates by end of file marker.

Output

For each test case, output an integer indicating the final points of the power.

Sample Input

3
1
50
500

Sample Output

0
1
15

先来简单说一下题意,就是给你一个n,然你找出1~n中有几个数满足内部含有连续的’49‘,比如4900,1495都算含有49,但是1409这种不含有连续’49‘的不算,题意就是这样,接下来我以这道题目为例,介绍一下数位dp。

看到这个数据量我们就知道这道题一定不是简单的枚举,而是要用数位dp来做,其实数位dp题目都大同小异,基本上套上板子都能够解决,先来介绍一下数组含义(以本道题目为例)

我们可以定义dp[pos][pre][flag1]数组  来表示前pos-1位数字已经确定且第pos位数字为pre

dp[pos][pre][0]表示 前pos-1位数字已经确定且第pos-1位数字为pre的还未达到目标(本题目标是含有子串49)的但第pos位及其之后都可以任选的方案数

dp[pos][pre][1]表示 前pos-1位数字已经确定且第pos-1位数字为pre的已经达到目标(本题目标是含有子串49)的但第pos位及其之后都可以任选的方案数

需要有一点说明,就是我们在利用dp数组或者更新dp数组时,一定要保证当前位可以任选数字

我们在函数中还要加一个变量来确定当前数字是否可以任选,只有在当前位置任选的情况下才能够使用记忆化数组储存的值,下面是代码,代码中有详细介绍:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<queue>
using namespace std;
typedef long long ll;
int a[300];
ll dp[30][10][2];
ll dfs(int pos,int pre,int flag1,int flag2)
{
	if(pos==0) return flag1;//函数递归出口 
	if(dp[pos][pre][flag1]!=-1&&flag2)//只有保证dp数组被更新过以及当前位的数字可以任选才能够使用记忆化数组储存的值 
		return dp[pos][pre][flag1];
	ll ans=0;//记录第pos位取不同值时的方案数之和,更新dp数组 
	int x=flag2?9:a[pos];//确定当前可以枚举的上限 
	for(int i=0;i<=x;i++)
		ans+=dfs(pos-1,i,(pre==4&&i==9)||flag1,i<x||flag2);//枚举下一位 
	//如果在某次函数调用后flag1变为1,说明到当前位置已经满足题目中所给条件,则之后一定会一直满足条件
	//如果在某次函数调用后flag2变为1,说明到当前位置已经可以任选,则之后一定可以任选(比如两个数字比大小,当一个数字的高位大于另一个数字,则不管低位是什么,高位大的一定大) 
	if(flag2) dp[pos][pre][flag1]=ans;//只有保证当前位置任选才能够更新记忆化数组储存的值 
	return ans;
}
ll solve(ll x)
{
	int tt=0;
	memset(dp,-1,sizeof dp);//每进行一次函数调用都要初始化一次 
	do
	{
		a[++tt]=x%10;
		x/=10;
	}while(x) 
	return dfs(tt,0,0,0);//函数调用,从高位往低位遍历 
}
int main()
{
	int T;
	ll n;
	cin>>T;
	while(T--)
	{
		scanf("%lld",&n);
		printf("%lld\n",solve(n));
	}
	return 0;
}

不同的数位dp题目一般只是题目要求的条件不同,所以对于不同的题目我们只需要更换一下要求的条件即可,有的还需要加一些判断条件,我在之后的博客中还会更新一些关于数位dp的题目。

如果大家对博主写的有什么问题的话,欢迎在评论区里提问。

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值