用天平称重时,我们希望用尽可能少的砝码组合称出尽可能多的重量。
如果只有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;
代码就不详细列出了,读者可以试试看,应该比较容易实现。