其他分享
首页 > 其他分享> > 【代码超详解】UVA 1374 Power Calculus 幂计算【IDDFS,120 ms】

【代码超详解】UVA 1374 Power Calculus 幂计算【IDDFS,120 ms】

作者:互联网

一、题目描述
给一个数 n 代表指数式 x^n ,你的任务是输出最小的得到该指数的乘除步数。
例:
输入 31 ,因为

所以输出为 6 (1 次乘法得到 pow(x, 2) ,4 次自身相乘得到 pow(x, 32) ,1 次除法达到目标 pow(x, 31) )。当输入 0 时,结束程序。

二、算法分析说明与代码编写指导
迭代加深搜索,将所有中间的计算结果保存到数组 buf 中,然后依次枚举下一步是乘已知的中间结果中的某个数还是除以这个数(对应指数的加减),然后继续搜索。搜索深度限制从 0 开始慢慢放开。
剪枝的条件一是搜索深度达到上限,判断指数 cur 是否等于输入值 n ,如果是,结束全部递归,输出步数 dmax ;条件二是如果当前指数 cur 在做二的(dmax - d)次乘方(即剩余搜索深度次乘方)以后仍然小于目标 n ,证明在 dmax 步数限制的条件下无法找到解。
注意枚举的范围是 buf[0] 到 buf[d] ,d 为已经进行的搜索深度,否则会在某些输入中死循环。当枚举到 cur = buf[d] + buf[d] 即 i = d 时,就构造出了 2 * cur 的已知中间结果。记得如果深入搜索无法找到解,要将对当前的指数 cur 的改动撤销。
如果在某个深度枚举完全部的已知变量还无法找到解,就要在枚举函数 Enumerate 的最后返回 false 。这回返回到浅一层或者直接返回到 main 函数,继续搜索。
另有 2 点注意:
1、如果只利用 2 的乘方作为中间值,会导致部分结果偏大。
2、本题必须采用有符号整数。因为在枚举过程中需要枚举当前指数 cur 与中间结果相减,进入深层搜索的过程中会将相减的结果也记录下来,在深层的枚举过程中将 buf[d] 与 buf[i] 相减就可能会出现负数。如果采用 unsigned 就会导致中间变量出现过大的数(比如相减结果 -1 记入 unsigned 变量就会变成 4294967295(0xFFFFFFFF)),从而超时。而采用有符号整数时,如果枚举出负数,就会被条件 cur × power[dmax - d] < n 挡住,回到原来深度继续枚举。大多数时候 unsigned 比 int 不快多少,如果保持 unsigned 类型而在剪枝条件中加入 cur > N(N 是一个很大的数,例如 4 000 000 000)反而在运行速度上得不偿失。
如果想查看已记录的中间计算结果,可以取消第 7 行被注释掉的语句。

三、AC 代码(120 ms)

#include<cstdio>
#pragma warning(disable:4996)
int n, dmax, cur; int power[32] = { 1 }, buf[100000] = { 1 };
inline bool Enumerate(const int& d) {
	if (d == dmax)return cur == n;
	if (cur * power[dmax - d] < n)return false;
	buf[d] = cur; //for (int i = 0; i <= d; ++i) { printf("%d ", buf[i]); }putchar('\n');
	for (int i = 0; i <= d; ++i) {
		cur = buf[d] + buf[i]; if (Enumerate(d + 1) == true)return true;
		cur = buf[d] - buf[i]; if (Enumerate(d + 1) == true)return true;
		cur = buf[d];
	}
	return false;
}
int main() {
	for (int i = 1; i <= 31; ++i)power[i] = power[i - 1] * 2;
	for (;;) {
		scanf("%d", &n); if (n == 0)return 0;
		for (dmax = 0;; ++dmax) { cur = 1; if (Enumerate(0) == true)break; }
		printf("%d\n", dmax);
	}
}

标签:Calculus,cur,Power,int,枚举,搜索,ms,dmax,buf
来源: https://blog.csdn.net/COFACTOR/article/details/99850704