题目描述
难度分:1400
输入n(1≤n≤109)。每次操作,你可以删除n的一个数字。需要保证删除后n仍然是一个没有前导零的正整数。你可以操作任意次。
问:把n变成一个完全平方数,至少需要操作几次?
输出操作次数。如果无法做到,输出−1。
输入样例1
8314
输出样例1
2
输入样例2
625
输出样例2
0
输入样例3
333
输出样例3
-1
算法
递归
先做一个预处理,将[1,n]中所有的完全平方数存入到一个哈希集合中。然后对n进行递归处理,对于每次递归,只要发现n在集合中就返回0。否则需要删除一位,枚举删除的数位,继续递归剩下的部分。
递归边界是n是一个个位数时可以直接检查它是否在完全平方数的集合中,对于一般情况,可以写成一个状态转移方程式dp[cur]=1+minnxtdp[nxt],其中nxt是去除掉cur的一个数位所表示的无前导零整数。最终答案就是dp[n],不过本题的数据量下没有必要做记忆化。
复杂度分析
时间复杂度
预处理的时间复杂度为O(√n);递归的第一层n有x位(x最大为10),第二层还剩x−1位,直到最后一层剩下1位,时间复杂度应该为O(x!)。因此,算法总体的时间复杂度为O(√n+x!)。
空间复杂度
递归层数的消耗只和n的位数有关,相比n的话很小,因此空间瓶颈是集合的消耗,额外空间复杂度为O(√n)
C++ 代码
#include <iostream>
#include <cstring>
#include <algorithm>
#include <unordered_set>
using namespace std;
typedef long long LL;
const int INF = 0x3f3f3f3f;
unordered_set<int> us;
int dfs(int n) {
if(n < 10) return us.count(n)? 0: INF;
if(us.count(n)) return 0;
int res = INF;
string str = to_string(n);
int sz = str.size();
for(int i = 0; i < sz; i++) {
string substr = str.substr(0, i) + str.substr(i + 1);
if(substr[0] != '0') {
int x = stoi(substr);
int nxt = dfs(x);
if(nxt != INF) res = min(res, 1 + nxt);
}
}
return res;
}
int main() {
int n;
scanf("%d", &n);
if(us.empty()) {
// 预处理
for(int i = 1; (LL)i*i <= n; i++) {
us.insert(i*i);
}
}
int res = dfs(n);
if(res == INF) res = -1;
printf("%d\n", res);
return 0;
}