题目大意:自己感觉非常奇葩的一道搜索题。给出n,求x^n。最先给出的两个数为x^1和x^0,可以执行如下操作:两个数相乘,其中一个数平方,两个数相除。问要达到x^n的最少步数。
思路:把此题说得简单一点就是给定两个加数a = 1, b = 0;(在这里我们规定a总是大于b)每一部可以执行a*2,b*2,a-b之一的操作,使得目标状态达到n,每次操作之后新得到的值要替换掉原来的一个数。例如当a = 16,b = 2我们可以得到以下状态:
a = 32,b = 2; a = 32, b = 16; a = 16, b = 4; a = 2,b = 4; a = 12 ; a = 18,b = 2; a = 18, b = 16; a = 14,b = 2; a = 16,b = 14;
然后剩下的就是搜索的问题了。
考虑以下情况可以剪枝:显然a = b是可以剪掉的;如果a超过目标状态n太多是没有最优解的,同理,b太大也是没有最优解的。只是我没有完全弄清楚为什么b大于100左右,a超过n 100 左右是没有最优解的。
#include<cstdio>
#include<iostream>
#define MAXN 800000
using namespace std;
struct T
{
int num1,num2,step;
}q[MAXN];
bool vis[20500][105];
int n;
int head,tail;
bool add(int num1,int num2,int step)//其实就是一个BFS,维护num1 > num2
{
if(num1 == n||num2 == n)//达到目标状态
{
return true;
}
if(num1 < num2)
swap(num1,num2);
if(num1 == num2||num1 >= n + 101|| num2 >= 101)//两个数相等,则无最优解;第一个数超过n太多无解;第二个数太大会运行错误
return false;
if(!vis[num1][num2])//入队列
{
vis[num1][num2] = 1;
tail++;
q[tail].num1 = num1;
q[tail].num2 = num2;
q[tail].step = step;
}
return false;
}
int main()
{
scanf("%d",&n);
head = tail = -1;
add(1,0,0);//初始状态的两个数,num1 = 1,num2 = 0
int num1,num2,step;
int ans;
while(head < tail)
{
++head;
num1 = q[head].num1,num2 = q[head].num2,step = q[head].step + 1;
if(add(num1+num1,num2,step) || add(num1,num2+num2,step) || add(num1,num1+num2,step) || add(num2,num1+num2,step) || add(num1,num1+num1,step) || add(num2,num2+num2,step) || add(num1 - num2,num2,step) || add(num1,num1 - num2,step))//考虑所有保和运算的情况,全部入队
{
ans = step;
break;
}
}
printf("%d\n",ans);
}