hdu 3555 Bomb(数位dp)

问题

hdu 3555 Bomb - https://acm.hdu.edu.cn/showproblem.php?pid=3555

分析

状态转移

从低位到高位

  • 注意:图中的最高位是指当前已构造部分的最高位,并非指最终的最高位
!9
!9
!9 且 !4
4
任意
9
9
9
最高位不是9,也不含49
最高位为9,但不含49
含49

从高位到低位

  • 注意:图中的最低位是指当前已构造部分的最低位,并非指最终的最低位
!4
!4
!9 且 !4
9
任意
4
4
4
最低位不是4,也不含49
最低位为4,但不含49
含49

“高 ⇒ \Rightarrow 低”枚举

  • 例如:求 5495 高 位 ← 低 位 \overset{高位 \leftarrow 低位}{5495} 5495以内的不含49的数字个数(包含:0000)
    • 最高高位取0时,则可取0999 - 0000内所有不含49的数
    • 最高高位取1时,则可取1999 - 1000内所有不含49的数
    • 最高高位取2时,则可取2999 - 2000内所有不含49的数
    • 最高高位取3时,则可取3999 - 3000内所有不含49的数
    • 最高高位取4时,则可取4999 - 4000内所有不是9开头的不含49的数
    • 最高高位取5时,则可取5495 - 5000内所有不含49的数
  • 如何求0999 - 0000、1999 - 1000、2999 - 2000、3999 - 3000、4999 - 4000、5495 - 5000内满足要求的数呢?
    • 0999 - 0000、1999 - 1000、2999 - 2000、3999 - 3000:所有999 - 000内的不含49的数
    • 4999 - 4000:所有999 - 000内的不是9开头的不含49的数(注意:90应看作090,不能看作以9开头)
    • 5495 - 5000:所有495 - 000内的不含49的数
    • 495 - 000是一个受限的3位数,而999 - 000 是一个不受限的3位数

dfs设计

  • 四类子问题
    • 不受限的m位不含49的数字个数【包含了9开头的不含49的数字】
    • 不受限的m位不是9开头的不含49的数字个数
    • 受限的m位不含49的数字个数【包含了9开头的不含49的数字】
    • 受限的m位不是9开头的不含49的数字个数(例如:495 - 000)
  • 参数
    • 数字位数, m m m
    • 是否受限, i s m a x ismax ismax
    • 是否包含9开头的不含49的数字, i s 4 is4 is4
  • 功能
    • 返回相应条件下的数字个数
    • 1、2两类子问题与状态表关系紧密,可以查表,也可以对表赋值

状态设计

  • 采用从低位到高位转移的方式
  • d p [ n ] [ s t a t e ] dp[n][state] dp[n][state]:长度n位的数字在state条件下 含49的数字个数
    • state = 0,表示所有不含49的数字个数,对应了dfs的第1类子问题
    • state = 1,表示最高位不是9的不含49的数字个数,对应了dfs的第2类子问题
  • dp数组 初始化 一次,也就是说状态表被多个测试样例 共享

代码

/* hdu 3555 Bomb 数位dp*/
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MXN = 25;
int n, b[MXN];
ll dp[MXN][2];
ll dfs(int m, int is4, bool ismax){
	if(m == 0) return 1;
	if(!ismax && ~dp[m][is4]) return dp[m][is4];
	ll ans = 0, end = ismax?b[m]:9;
	for(int i = 0; i <= end; ++i){
		if(is4 && i == 9) continue;
		ans += dfs(m-1, i == 4, ismax && i == end);
	}
	if(!ismax) dp[m][is4] = ans;
	return ans;
}
int main(){
	int t;
	ll N;
	scanf("%d", &t);
	memset(dp, -1, sizeof dp); // 仅初始化一次
	while(t--){
		scanf("%lld", &N);
		n = 0;
		for(ll i = N; i > 0; i /= 10) b[++n] = i%10;
		printf("%lld\n", N+1-dfs(n, 0, true));
	}
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

jpphy0

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值