UVa 1374 Power Calculus

Starting with x and repeatedly multiplying by x, we can compute x31 with thirty multiplications:

x2 = x x x,      x3 = x2 x x,      x4 = x3 x x,      ...  ,      x31 = x30 x x.
The operation of squaring can appreciably shorten the sequence of multiplications. The following is a way to compute  x31  with eight multiplications:
x2 = x x x,      x3 = x2 x x,      x6 = x3 x x3,      x7 = x6 x x,      x14 = x7 x x7
x15 = x14 x x,      x30 = x15 x x15,      x31 = x30 x x.
This is not the shortest sequence of multiplications to compute  x31 . There are many ways with only seven multiplications. The following is one of them:
x2 = x x x,      x4 = x2 x x2,      x8 = x4 x x4,      x10 = x8 x x2
x20 = x10 x x10,      x30 = x20 x x10,      x31 = x30 x x.
There however is no way to compute  x31  with fewer multiplications. Thus this is one of the most efficient ways to compute  x31  only by multiplications.

If division is also available, we can find a shorter sequence of operations. It is possible to compute x31 with six operations (five multiplications and one division):

x2 = x x x,      x4 = x2 x x2,      x8 = x4 x x4,      x16 = x8 x x8,      x32 = x16 x x16,      x31 = x32 ÷ x.
This is one of the most efficient ways to compute  x31  if a division is as fast as a multiplication.

Your mission is to write a program to find the least number of operations to compute xn by multiplication and division starting with x for the given positive integer n. Products and quotients appearing in the sequence of operations should be x to a positive integer's power. In other words, x-3, for example, should never appear.

Input 

The input is a sequence of one or more lines each containing a single integer nn is positive and less than or equal to 1000. The end of the input is indicated by a zero.

Output 

Your program should print the least total number of multiplications and divisions required to compute xn starting with x for the integer n. The numbers should be written each in a separate line without any superfluous characters such as leading or trailing spaces.

Sample Input 

1
31
70
91
473
512
811
953
0

Sample Output 

0
6
8
9
11
9
13
12
 
#include <cstdio>
#include <cstring>
#include <cmath>
using namespace std;

long long array[1200];
int array_count = 0;

int n;

bool dfs_search(int now_d, int max_d);

int main()
{
	while(scanf("%d", &n) && n != 0)
	{
		memset(array, 0, sizeof(array));
		array[0] = 1;
		array_count = 0;
		array_count++;
	
		if(n == 1)	
		{
			printf("0\n");		
			continue;
		}
		else if(n == 2)
		{
			printf("1\n");
			continue;
		}	
		// 迭代加深搜索
		array[1] = 2;
		array_count++;
		for(int i = 2; i <= n-1; i++)
		{
//			printf("here: %d\n", i);
			if(dfs_search(1, i))
			{
				printf("%d\n", i);
				break;
			}	
		}				
	}	
	return 0;	
}

// 迭代加深搜索
bool dfs_search(int now_d, int max_d)
{
	if(now_d == max_d)
	{
		if(array[now_d] == n)
			return true;
		else
			return false;
	}	
	else 
	{
		long long max = 0;
		for(int i = 0; i < array_count; i++)
		{
			if(array[i] > max)
				max = array[i];
		}	
		if(pow(2, max_d-now_d)*max < n)
			return false;	
	}
	
	int i = array_count-1;
		for(int j = 0; j < array_count; j++)
		{
			long long new_num1 = array[i]+array[j];
			long long new_num2 = abs(array[i]-array[j]);
			int find_flag1 = 0;
			int find_flag2 = 0;
			for(int k = 0; k < array_count; k++)
			{
				if(array[k] == new_num1)
					find_flag1 = 1;
				if(array[k] == new_num2)
					find_flag2 = 1;
			}

			array[array_count] = new_num1;
			array_count++;
			if(dfs_search(now_d+1, max_d))
				return true;
			array_count--;
			if(new_num2 > 0 && new_num2 != new_num1)
			{
				array[array_count] = new_num2;
				array_count++;
				if(dfs_search(now_d+1, max_d))
					return true;
				array_count--;
			}
		}
	return false;	
}


本题是第一道独立完成的迭代加深搜索,鼓励一下!
剪枝是根据得到的最大的数乘以2^(还能搜的层数),如果这个数小于n, 就剪枝。
另外,本题的搜索中每次都只搜索上一层刚刚得到的那个数和其他数的和或差,而不是任取两个数。
原因是:假设最终得到的数为a = b +/- c, 如果b和c都不是倒数第二次得到的数,那么就会有一个序列(除了倒数第二个数不生成,其他和本序列相同),
得到最终的数a,但是长度减小1.同理可证倒数第二个数的生成必定有倒数第三个数来参与。
所以第i次生成的数,必定有第i-1次生成的数来参与生成。
 
刘汝佳的算法竞赛入门经典(第二版)P210说无法给出证明,不知为何。

                
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值