Digit Products

一,题目

翻译:从所有不小于N的正整数中找到满足数位上的乘积不超过K的数的个数。

链接:E - Digit Products (atcoder.jp)

二,思路

 数位dp老套路,先写记忆化搜索模板 

int dfs(int pos, int limit, int lead, int sum)
{
	int num = getsum(sum); 
	if (pos == 0) return num <= K;
	if (!limit && !lead && book[pos].count(num)) return book[pos][num];
	int maxn = limit ? arr[pos] : 9, ans = 0;
	for (int i = 0; i <= maxn; ++i)
	{
		//if (num * i > K) continue;
		ans = ans + dfs(pos - 1, limit && i == maxn, lead && i == 0, (sum * 10 + i));
	}
	if (!limit && !lead) book[pos][num] = ans;
	return ans;
}

很明显,完全很套路,只不过book从存sum变成存num(sum指现在枚举的数,num指sum的数位上的乘积)

三,需要解决的地方

1,由于sum可能很大,达到1e18,这时我们不可能用book数组存储sum,就算unordered_map和map都很难存储,这时,我们可以用num,即sum的数位上的乘积,很明显由于K的限制,用map或unordered_map来构建的book不会存储超过1e9的num

unordered_map<int, int> book[100];

2,用记忆化搜索查找的数是从0到N,而题目要求查找的是1到N,所以最后输出要减去1

cout << handle(N) - 1;

3,由于记忆化搜索直接使用num(num指sum的数位上的乘积),而不使用sum的话,代码会写得很麻烦,所以dfs这里最好用sum传参

直接使用num

 不使用num

 4,下面的代码中,由于直接使用map或unordered_map构造book函数,所以不要使用book[pos][num]!=0或book[pos][num]!=-1,而是使用book[pos].count(num).

if (!limit && !lead && book[pos].count(num)) return book[pos][num];

(注:请不要把下面注释的代码加上,你会搞半天都不知道为什么连测试样例都过不了,不要问我为什么)

 四,代码

#define _CRT_SECURE_NO_WARNINGS 1
#pragma GCC optimize(1)
#pragma GCC optimize(2)
#pragma GCC optimize(3,"Ofast","inline")
#include<iostream>
#include<algorithm>
#include<cstdlib>
#include<math.h>
#include<cmath>
#include<string>
#include<unordered_map>
#include<set>
#include<vector>
#include<queue>
#include<cstring>
#include<string>
#include<iostream>
using namespace std;
#define endl '\n'
#define int long long
const int mod = 1e9 + 7;
int N, K;
int arr[100] = { 0 };
unordered_map<int, int> book[100];
int getsum(int sum)
{
	if (sum == 0) return 0;
	int pro = 1;
	while (sum) pro = pro * (sum % 10), sum /= 10;
	return pro;
} 
int dfs(int pos, int limit, int lead, int sum)
{
	int num = getsum(sum); 
	if (pos == 0) return num <= K;
	if (!limit && !lead && book[pos].count(num)) return book[pos][num];
	int maxn = limit ? arr[pos] : 9, ans = 0;
	for (int i = 0; i <= maxn; ++i)
	{
		//if (num * i > K) continue;
		ans = ans + dfs(pos - 1, limit && i == maxn, lead && i == 0, (sum * 10 + i));
	}
	if (!limit && !lead) book[pos][num] = ans;
	return ans;
}
int handle(int num)
{
	int cnt = 0;
	while (num) arr[++cnt] = num % 10, num /= 10;
	return dfs(cnt, 1, 1, 0);
}
signed main()
{
	ios::sync_with_stdio(0); cout.tie(0); cin.tie(0);
	cin >> N >> K;
	cout << handle(N) - 1;
	return 0;
}

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值