(数位dp)HDU3555 Bomb

HDU3555 Bomb

题意&思路:

给个n,计算1~n中子串含有“49”的数。
对于这种求一个区间内含有某种特征的数,一般用数位dp。数位dp就是数字的位上dp,例如这题给的范围是到263-1约等于1019,如果枚举遍历判断的话,肯定会T。如果是数位的话,就只要枚举19位的情况。
听起来是不是感觉很简单 实在是太难了,看了好多大佬的题解和分析才懂了一点点。
数位dp借助于dfs和记忆化搜索。就像我们平时数数一样我们是从个位往十位往百位……数的,我们从最高位往低位搜索,然后回溯。记忆化搜索就相当于对于十位的1,2……来说符合的个位数是相同的,对于往上的位数也是如此。
我们用dp[i][j]表示i位上是否符合我们所需要的状态。对于一个数位的枚举,例如2451,对于1~2000来说个位到百位都可以取0~9,但是对于2000~2400,像百位只能取到4,2400~2450十位只能取到5,2450~2451个位只能取到1,这样我们就要对每个位数有个限制limit。
我们先将数字的位数拆分,以便得到每位数上界的值。
再看dfs中的三个参数,len是数字的位数,p4表示上一位是不是4,limit表示是否是数位上界。
下面的程序:
len==0时,就肯定枚举到底部了,那么返回1,表示这个数符合要求。
当不是数位上届,并且当前这种状态已经求过值了,就直接返回,例如对于十位的2来说,之前的1已经求到dp[1][0]=10了,所以直接返回值就可以了。
up=limit?a[len]:9用于判断数字的上界。
下面就是枚举每个位数,如果遇到这个位是9,并且上一位是4的就跳过。
dfs(len-1,i==4,limit && i==up)就是状态的转移,长度,此位是否等于4,已经是否是数位上界。if(!limit) dp[len][p4]=tmp,是记忆化的过程。

这题我们求不含49的数,然后再减去他就可以了。

代码:

#include<bits/stdc++.h>
#define pii pair<int,int>
#define ll long long
const int N=1e6+10;
const int mod=1e7+9;
const int maxn=0x3f3f3f3f;
const int minn=0xc0c0c0c0;
const int inf=99999999;
using namespace std;
ll dp[20][2],a[20];
ll dfs(int len,bool p4,bool limit)
{
	if(len==0)
		return 1;
	if(!limit && dp[len][p4])
		return dp[len][p4];
	ll tmp=0,i,up=limit?a[len]:9;
	for(i=0;i<=up;i++)
	{
		if(p4 && i==9)
			continue;
		tmp+=dfs(len-1,i==4,limit && i==up);
	}
	//if(!limit)
		dp[len][p4]=tmp;
	return tmp;
}
ll solve(ll x)
{
	ll l=0;
	while(x)
	{
		a[++l]=x%10;
		x/=10;
	}
	return dfs(l,false,true);
}
int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	int t;
	cin>>t;
	while(t--)
	{
		ll n,ans;
		memset(dp,0,sizeof(dp));
		cin>>n;
		ans=n+1-solve(n);
		cout<<ans<<endl;
	}
	return 0;
}

发布了78 篇原创文章 · 获赞 0 · 访问量 1409

猜你喜欢

转载自blog.csdn.net/Z7784562/article/details/103899582
今日推荐