HDU 3555 Bomb + HDU 2089 不要62 数位dp入门题目

本文介绍数位DP的基础概念及应用,通过两个实例讲解如何解决特定数值范围内满足特定条件的数的数量问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

第一次接触数位dp的问题,在这里记录一下我个人的学习思路,如果能够对大家有什么帮助那就是最好的了。

动态规划也是刚刚开始学习,说实话对这种类型的题目现在还没有太适应。。。

数位dp通常都是类似这种叙述的问题:给定区间[l , m],求解其中满足(不满足)条件 p 的解集大小。

初始化的时候会开一个数组dp[n][10],dp[i][j] 表示第 i 位上为数字 j 并且符合(不符合)条件 p 的数目。一般用一个三重循环来搞定初始化的数值:

(pseudo code is as follow)

	dp[0][0] = 1;
	for (int i = 1; i < n; i++)
	{
		for (int j = 0; j < 10; j++)
		{
			for (int k = 0; k < 10; k++)
			{
				if (p)
				{
					dp[i][j] += dp[i - 1][k];
				}
			}
		}
	}

第一重循环是n - 1位数位完全遍历,第二重和第三重循环分别是第 i 位和第 i - 1 位数位上0~9的数值。

dp[i][j] = dp[i][j] + dp[i - 1][k] 的含义是,当前位解集的大小为满足当前条件的数目和上一位解集大小之和(将这个过程联想成一棵树形的迭代结构)。接下来的过程就是根据题目具体描述来进行分析了。

HDU-2089 不要62 问题概述:给定数值范围n,m;求其中不是不吉利的数有多少个(不含4并且不含62)

AC代码如下

#include<iostream>
using namespace std;
//#include<cmath>
//#include<string>

long long dp[10][10];

void init()
{
	dp[0][0] = 1;
	for (int i = 1; i < 10; i++)
	{
		for (int j = 0; j < 10; j++)
		{
			for (int k = 0; k < 10; k++)
			{
				if (j != 4 && !(j == 6 && k == 2))
				{
					dp[i][j] += dp[i - 1][k];
				}
			}
		}
	}
}

long long solution(long long n)
{
	int digit[15];
	int len = 0;
	long long mem = n, ans = 0;
	while (n > 0)
	{
		digit[++len] = n % 10;
		n /= 10;
	}
	digit[len + 1] = 0;
	for (int i = len; i > 0; i--)
	{
		for (int j = 0; j < digit[i]; j++)
		{
			if (!(digit[i+1] == 6 && j == 2) && j != 4)
			{
				ans += dp[i][j];
			}
		}
		if ((digit[i] == 2 && digit[i + 1] == 6) || digit[i] == 4)
		{
			break;
		}
	}
	return ans;
}

int main()
{
	init();
	long long n, m;
	while (cin >> n >> m)
	{
		if (n == 0 && m == 0)
		{
			break;
		}
		cout << solution(m + 1) - solution(n)<< endl;
	}
	//system("pause");
	return 0;
}

相邻两位进行判断即可

HDU-3555 Bomb 问题概述:输入最大倒计时数n,求炸弹能量增加的最大值(求小于n并且含49的数字有多少个)

#include<iostream>
using namespace std;
//#include<cmath>
//#include<string>

long long dp[20][10];

void init()
{
	dp[0][0] = 1;
	for (int i = 1; i < 20; i++)
	{
		for (int j = 0; j < 10; j++)
		{
			for (int k = 0; k < 10; k++)
			{
				if (!(j == 4 && k == 9))
				{
					dp[i][j] += dp[i - 1][k];
				}
			}
		}
	}
}

long long solution(long long n)
{
	int digit[20];
	int len = 0;
	long long mem = n, ans = 0;
	while (n > 0)
	{
		digit[++len] = n % 10;
		n /= 10;
	}
	digit[len + 1] = 0;
	for (int i = len; i > 0; i--)
	{
		for (int j = 0; j < digit[i]; j++)
		{
			if (!(digit[i+1] == 4 && j == 9))
			{
				ans += dp[i][j];
			}
		}
		if ((digit[i] == 9 && digit[i + 1] == 4))
		{
			break;
		}
	}
	return mem - ans;
}

int main()
{
	init();
	int t;
	long long n;
	cin >> t;
	while (t --)
	{
		cin >> n;
		cout << solution(n + 1) << endl;
	}
	//system("pause");
	return 0;
}
整体和2089的思路一样,换了个判断条件而已。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值