【习题·搜索】Power Hungry Cows(启发式搜索A*+剪枝)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Ronaldo7_ZYB/article/details/89075415

题目描述

FJ的奶牛想要快速计算整数P的幂 (1 <= P <=20,000),它们需要你的帮助。因为计算极大数的幂,所以它们同一时间仅能使用2个存储器,每个存储器可记录某个结果值。 第一件工作是初始化存储器内的值一个为底数x, 另一个为1。 奶牛可以相乘或相除2个存储器中的值,并把结果存在其中某个存储器内,但所有存储的结果必须是整数。 例如, 如果他们想计算x^31, 一种计算方法是:

WV1 WV2
开始: x 1
存储器1和存储器1相乘,结果存于存储器2: x x^2
存储器2和存储器2相乘,结果存于存储器2: x x^4
存储器2和存储器2相乘,结果存于存储器2: x x^8
存储器2和存储器2相乘,结果存于存储器2: x x^16
存储器2和存储器2相乘,结果存于存储器2: x x^32
存储器2除以存储器1,结果存于存储器2: x x^31
因此, x^31可以通过6次计算得出。给出要计算的幂次,要求求出最少需要几次计算。

输入格式
仅一个整数: P。

输出格式
仅一个整数:最少计算次数。

样例数据
input

31
output

6

题目大意

有两个数,分别是 1 1 0 0 ;每次选取其中两个相同或不同的数相加或相减,每次替换掉其中的一个数,并在保证每一个数都是非负数的前提下不断进行这样的操作,问至少要操作多少次才能使其中的一个数变成n。

题解

这道题由于边权为 1 1 ,可以选择使用正常的广度优先搜索算法;但是搜索状态有 a n s 10 ans^{10} ,因此我们需要用其他算法来解决此题。

我们可以使用启发式 A A* 算法来解决,可以设计估价函数为:

  • f ( n o w ) = n f(now)=当前状态的任意一个数\ge n的最小步数
  • 由于自加最快,不断对最大数进行*2操作即可。
  • 一定可以保证优于最优策略:如果步数 = n =n 不用说,直接可以到达最优决策。如果操作步数 &gt; n &gt;n ,要么通过若干次相减得到,要么直接无解,但是一定可以小于最优决策。

但是这样的 A A* 算法仍然有超时的可能,我们还需要对此进行一系列的剪枝,对于每一个状态 ( x , y , v ) (x,y,v) ,表示两个数分别为 x , y x,y 且满足 x &gt; y x&gt;y ,操作步数为 v v ,则有:

  • 如果 x x y y 是负数,则是一组非法解。
  • 如果 x &gt; n x&gt;n y = 0 y=0 ,则不论如何都不能达到 n n x x 无法通过 y y 变小,只能通过自减变成 0 0 ;若如此,则无法通过自身与 y y 变大达到 n n )。
  • 如果 n   m o d   g c d ( x , y )   = ̸   0 n\ mod\ gcd(x,y)\ =\not\ 0 ,则无法通过加减操作得到最优解(此时需要意会 )。
  • 如果 x   =   y x\ =\ y ,则相当于状态 ( x , z ) , x = ̸ z (x,z),x=\not z 的自加自减操作,是一种等效的状态,因此可以剪枝。
  • 显然对于每一个状态到需要用 H a s h Hash 算法解决;如果当前状态的步数 \ge 该状态的最优步数, 则剪枝。

注意

  • 对于 A A* 算法,要设计两个关键字;第一关键字为深度+估价函数,第二关键字为估价函数。因为第二关键字越少,基于底层的搜索层数也越少,可以在很大情况下减少搜索树的规模。

代码如下:

#include <bits/stdc++.h>

using namespace std;

const int N = 1000000;
const int V = 100000;
int n,tot,ans;
int Link[V],X[N],Y[N],Next[N],v[N];

struct node
{
	int x,y,v;
	int f (void)
	{
		int tx = x;
		int cnt = 0;
		while (tx<n) tx<<=1, cnt ++;
		return cnt;
	}
	friend bool operator < (node a,node b)
	{
		//if (a.v+a.f() ^ b.v+b.f()) 
			return a.v+a.f() > b.v+b.f();
		//else 
		//	return a.f() > b.f();
	}
};

priority_queue < node > q;

inline int Hash (int x,int y)
{
	int num = x*47+y*991;
	return num%99991;
} 

void add (int x,int y,int num)
{
	tot ++;
	Next[tot] = Link[Hash(x,y)];
	X[tot] = x;
	Y[tot] = y;
	v[tot] = num;
	Link[Hash(x,y)] = tot;
}

int ask (int x,int y)
{
	for (int i=Link[Hash(x,y)];i;i=Next[i]) 
	    if (x == X[i] && y == Y[i]) 
	        return v[i];
	return -1;
}

pair<int,int> New(int x,int y,int num)
{
	if (num == 0) return make_pair(x+x,y);
	if (num == 1) return make_pair(x+x,x);
	if (num == 2) return make_pair(y+y,y);
	if (num == 3) return make_pair(max(y+y,x),min(y+y,x));
	if (num == 4) return make_pair(x+y,y);
	if (num == 5) return make_pair(x+y,x);
	if (num == 6) return make_pair(x-y,x);
	if (num == 7) return make_pair(max(x-y,y),min(x-y,y));
	if (num == 8) return make_pair(y,0);
	if (num == 9) return make_pair(x,0);
}

int gcd(int a,int b)
{
	if (b == 0) return a;
	return gcd(b,a%b);
}

int main(void)
{
	freopen("power.in","r",stdin);
	freopen("power.out","w",stdout);
	tot = 0;
	cin>>n;
	add(1,0,0);
	q.push(node{1,0,0});
	while (q.size())
	{
		node top=q.top();
		q.pop();
		if (top.x == n || top.y == n) 
		{
			ans = top.v;
			break;
		} 
		if (top.v > ask(top.x,top.y)) continue;
		for (int i=0;i<10;++i)
	    {
	    	pair<int,int> Next = New(top.x,top.y,i);
	    	int nx = Next.first;
	    	int ny = Next.second;
	    	if (nx < 0 || ny < 0) continue;
			if (nx == ny) continue;
	    	//等效状态 
	    	if (ask(nx,ny) ^ -1 && ask(nx,ny) < top.v+1) continue;
	    	//相同状态下访问步数少 
	    	if (n % gcd(nx,ny)) continue;
	    	//无解 
	    	if (nx > n && ny == 0) continue;
	    	//无解 
	    	add(nx,ny,top.v+1);
	    	q.push(node{nx,ny,top.v+1});
	    }
	}
	cout<<ans<<endl;
	return 0;
}

猜你喜欢

转载自blog.csdn.net/Ronaldo7_ZYB/article/details/89075415