给出n,求x^n。最先给出的两个数为x^1和x^0,可以执行如下操作:两个数相乘,其中一个数平方,两个数相除。问要达到x^n的最少步数。
分析
每一个状态是由最多两个数转化而来的,所以可以把两个数看做一个状态。
我们假设 {x,y,g,h} x为当前状态较大的数,y为较小的
g为到当前状态的步数,h为至少还需要多少步(x自乘最少要多少次)
用优先队列取出g+h最小的,然后增广
当第一次搜到x,y有一个是n时g就是答案
[剪枝]
1.x>2*n 明明不用乘上去非要乘上去....(不是最优解)
2.x>n&&y==0 回不到n(不是最优解)
3.x>n && y>n 不是最优解
4.n%gcd(x,y)>0 得不到答案
5.x==y 不是最优解
因为该状态和一个x的状态对未来的贡献是等价的,反正自乘自除也能达到一样的效果,不管y取什么数,都比x与y相等时更优。
#include<iostream>
#include<map>
#include<queue>
#include<algorithm>
#define N 20005
using namespace std;
struct Node{
int x,y,g,h;
bool operator < (const Node &a) const{
return g+h==a.g+a.h ? g>a.g : g+h>a.g+a.h;
}
};
priority_queue<Node> q;
int n,g,h;
map<pair<int,int>,int> M;
int gcd(int x,int y){return y?gcd(y,x%y):x;}
void Push(int x,int y){
if(x<y) swap(x,y);
if(x>2*n) return;
if(x>n&&y==0) return;
if(x>n&&y>n) return;
if(x==y) return;
if(M[make_pair(x,y)]&&M[make_pair(x,y)]<g+1+h) return;
if(n%gcd(x,y)) return;
int h1=0,k=x;
while(k<n) h1++,k*=2;
M[make_pair(x,y)]=g+1+h1;
q.push((Node){x,y,g+1,h1});
}
int bfs(){
Push(1,0);
while(!q.empty()){
Node cur=q.top(); q.pop();
int x=cur.x,y=cur.y;
g=cur.g,h=cur.h;
if(x==n||y==n) return g;
Push(x,x-y),Push(y,x-y),Push(x,x+y),Push(y,x+y);
Push(y,x*2),Push(x,y*2),Push(x,x*2),Push(y,y*2);
}
}
int main(){
cin>>n;cout<<bfs()-1;return 0;
}