一,题目
翻译:从所有不小于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;
}