题目
题意
已知n和x,可以对整数x执行多个操作。
操作:选择在x出现过的任何数字y(y只有1位数),然后用x替换x⋅y
你想使x的十进制表示长度(不带前导零)等于n。要做到这一点,最少需要多少操作?
如果不可能,则为-1。
两种方法对比
其实此题更适合用bfs,保证第一次都出来的就是最小值,不会走太多太多的冤枉路时间,复杂度低;其次,bfs的代码也比较简单与常规,只需要简单标记一下,不去重复之前走过的路。
反观dfs,每一次都走到头,需要遍历所有的方式才能找到最小值;其次,dfs剪枝是个重点,其中需要涉及到数学中的log运算。解决了这一点的话,dfs也蛮简单的。
说一下一些小知识的收获
1. to_string(x).size() //可以求出数字x的长度,很方便
2. 本题所求需要一直比较是否x达到n位,简化一下就是x>=pow(10,n-1)
3. log(x1) 除 log(x2) = log以(x2)为底(x1)的对数
4. 数少的时候,用一个数的二进制标记也好帅的。
dfs方法
题解
讲一下这个式子 timee+ 1 + log(tar/x)/log(9)
timee是到现在为止已经操作的次数。
log(tar/x)/log(9) 是当前x成为tar所需要至少至少的操作数(为什么是至少,因为是以9为底,即假设每次都是乘以9)。
+1是因为log那位出来的基本上都是小数,打了几次表没出现过整数,抱着进位的思想,所以加1。
注意:以上少了任意一步都无法剪枝成功
代码
#include <bits/stdc++.h>
using namespace std;
const int N = 2e5+10;
typedef long long LL;
int chushi;
LL n, x,tar;
int res =999999999;
void dfs(LL x, int timee)
{
if(timee+1 + log((double)tar/x)/log(9)>= res) return;
if(x >= tar)
{
res = min(res, timee);
return;
}
LL now = x;
int s=0;//标记有这个数,这样遍历的时候,就不会走x*出现的数了
while(now) s |=(1<<(now%10)), now/=10;
for(int i =9; i >= 2; i --)
if((s>>i) & 1)
dfs(x*i, timee+1);
return;
}
int main()
{
cin >> n >> x;
tar = pow(10,n-1);
dfs(x,0);
if(res != 999999999) cout << res<<endl;
else puts("-1");
return 0;
}
bfs方法
题解
这个是常规操作,大家记得开long long
代码
#include <iostream>
#include <String>
#include <map>
#include <cmath>
#include <queue>
typedef long long LL;
using namespace std;
int n;
LL x,tar;
map<LL,bool> mp;
int bfs(LL xx, int times)
{
queue<pair<LL, int>> q;
q.push({xx, times});
while(q.size())
{
LL t1 = q.front().first;
int t2 = q.front().second;
q.pop();
if(tar <= t1) return t2;//位数达到n了
LL tt1 = t1;
while(t1)
{
LL now = (t1%10) * tt1;
if(t1%10>=2 && !mp[now])
q.push({now, t2+1}), mp[now]=1;
t1/=10;
}
}
return -1;
}
int main()
{
cin >> n >> x;
tar = pow(10, n-1);
cout <<bfs(x, 0)<<'\n';
return 0;
}