【题目链接】
CodeVS 2541 幂运算
【题目描述】
从m开始,我们只需要6次运算就可以计算出$m^{31}$:
$m^2=m×m,m^4=m^2×m^2,m^8=m^4×m^4$, $m^{16}=m^8×m^8,m^{32}=m^{16}×m^{16},m^{31}=m^{32}÷m $。
请你找出从$m$开始,计算$m^n$的最少运算次数。在运算的每一步,都应该是$m$的正整数次方,换句话说,类似$m^{-3}$是不允许出现的。 (1<=n<=1000)
【解题思路】
这题比较简单,以已经得到的指数集合为状态,扩展操作是在其中任选两个数做加减法,且不要产生重复的数(产生重复的数显然不合算)。
考虑简单剪枝,如果记当前的得到的最大指数$max$, 当$max * 2^{limit - d} < n$时剪枝,含义就是一直用最大的数做加法,到达深度上限时指数也到不了$n$,这实际上也是IDA*算法,只不过乐观估价函数为对数式,为方便计算将不等式变形为指数式。
另外的,我们有一个未能证明的优化,每次总是使用"刚刚得到"的那个指数,这个优化的正确性未能证明,但1000以内不存在反例。
【AC代码】
#include <cstdio>
#include <algorithm>
#define MAXN 1000
int n;
int exp[MAXN]; // 指数集合
int limit;
bool dfs(int d, int max){
if(exp[d] == n) return true;
if(d == limit) return false;
if((max << limit - d) < n) return false; // 剪枝
for(int i = d; i >= 0; i--){
exp[d + 1] = exp[d] + exp[i]; // 考虑加法
if(dfs(d + 1, std::max(exp[d], exp[d + 1]))) return true;
exp[d + 1] = exp[d] - exp[i]; // 考虑减法
if(dfs(d + 1, std::max(exp[d], exp[d + 1]))) return true;
}
// 这里都使用了数字exp[d],即刚刚得到的数字。
return false;
}
inline int solve(){
if(n == 1) return 0;
exp[0] = 1;
for(limit = 1; limit < MAXN; limit++){
if(dfs(0, 1)) return limit;
}
}
int main(){
scanf("%d", &n);
printf("%d", solve());
return 0;
}
就这样啦