五个砝码问题,三种解法

5个砝码
用天平称重时,我们希望用尽可能少的砝码组合称出尽可能多的重量。
如果只有5个砝码,重量分别是1,3,9,27,81。则它们可以组合称出1到121之间任意整数重量
(砝码允许放在左右两个盘中)。
本题目要求编程实现:对用户给定的重量,给出砝码组合方案。
例如:
用户输入:
5
程序输出:
9-3-1
用户输入:
19
程序输出:
27-9+1

  要求程序输出的组合总是大数在前小数在后。

    可以假设用户的输入的数字符合范围1~121。

方法一:递归求解

可以发现,在输出的表达式中第一个输出的数不可能是负数,所以首先我们从最大的数81开始,直到得到满足题意的数。如:输入5,输出9-3-1,第一个满足的是9;输入19,输出27-9+1,第一个数是27等。确定第一个数之后再与比这个数小的数(最接近这个数的数)相加,若不满足则相减,然后取满足的运算结果继续重复之前的运算。

代码如下:

#include<stdio.h>
int n;//输入的数
int temp[6] = {0,1,3,9,27,81};//保存5个砝码
//result保存当前运算结果,i表示当前所需最大砝码下标,
//sum的作用是这样的:当我们选择81的时候,其余数之后为sum=1+3+9+27,如果81-40大于n,
//则结束后面的递归。例如输入5,从砝码81开始判断,直到砝码9才满足,此时sum=1+3=4,因
//为9-sum=9-4=5不大于5,所以满足,继续后面的运算。sum就是保存小于当前所选砝码的重量和。
//can数组用来保存满足题意的计算过程。
void fun(int result,int i,int sum,int can[])
{
	if (result == n)//如果运算结果等于n
	{
		for (int k = 5; k > 0; k--)
		{
			if (can[k] > 0)
			{
				printf("+%d",can[k]);
			}
			else if (can[k] < 0)
			{
				printf("%d",can[k]);
			}
		}
		printf("\n");
	}
	else if (result - sum > n)//判断有没必要继续后面的计算
	{
		return;
	}
	else
	{
		for (int j = i; j > 0; j--)
		{
			can[i] = temp[j];//保存过程
			fun(result + temp[j],j - 1,sum -= temp[j],can);//相加
			if (i == 5)//第一个打印输出的数是正数,不可能用来相减,是i==5时开始的
			{
				continue;
			}
			can[i] = -temp[j];//保存过程
			fun(result - temp[j],j - 1,sum,can);//相减
			can[i] = 0;//都不满足,归零
		}
	}
}
int main()
{
	int can[6] = {0};
	scanf("%d",&n);
	fun(0,5,121,can);//开始递归求解
	return 0;
}

方法二:迭代法

这个看下代码应该就明白了,代码如下:

#include <stdio.h>
int main()
{
	int can[5];
	//每个砝码有三种状态,要么相加,要么相减,要么啥也不干
	int a[3] = {0,1,-1};
	int b[3] = {0,3,-3};
	int c[3] = {0,9,-9};
	int d[3] = {0,27,-27};
	int e[3] = {0,81,-81};
	int h,i,j,k,m,n;
	scanf("%d",&n);
	for (i = 0; i < 3; i++)
		for (j = 0; j < 3; j++)
			for (k = 0; k < 3; k++)
				for (m = 0; m < 3; m++)
					for(h = 0; h < 3; h++)
					{
						if (a[i] + b[j] + c[k] + d[m] + e[h] == n)
						{
							can[4] = a[i];
							can[3] = b[j];
							can[2] = c[k];
							can[1] = d[m];
							can[0] = e[h];
						}
					}
	for (i = 0; i < 5; i++)
	{
		if (i == 0)
			printf("%d",can[i]);
		else if (can[i] > 0)
			printf("+%d",can[i]);
		else if (can[i] < 0)
			printf("%d",can[i]);
	}
	printf("\n");
	return 0;
}

方法三:巧妙利用3进制

首先,为啥用3进制呢,可以看看题目这组数据,你就会发现:3^0 = 1,3^1 = 3,3^2 = 9,3^3 = 27,3^4 = 81。

然后,将输入的数转换成3进驻,如19的三进制为201,我们同时可以发现,每一个三进制位上的数表示有几个相应的砝码。如三进制201表示有

2个砝码9,一个砝码1,所以2 * 9 + 1 = 19。同样我们如果输入5,三进制为12,表示有一个砝码3,两个砝码1,所以3 + 2 * 1 = 5。但题目要求每个砝码

只用一次,那怎么办呢?你可以发现三进制每个位上只有三种可能,要么是0,要么是1,要么是2,再想想方法二,是不是明白得差不多了?三种状态如何处理呢?

首先转换成三进制,然后从末尾起查看每一位,对于第i位来讲(i从0开始)
1. 如果是1,则相当于用3的i次方1次
2. 如果是2,则相当于用3的i次方作为减法1次,并且向高位进1
3. 如果是0,则不需要用到3的i次方

如输入19,三进制为201,从最后一位开始,首先是1,则记+3^0;第二位为0,不处理;第三位为2,则记-3^2,向高位进一,那么第四位从0变成1,记+3^3。

一综合即为:3^3 - 3^2 + 3^0 = 27 - 9 + 1 = 19;

代码就不详细列出了,读者可以试试看,应该比较容易实现。

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值