这个问题确实又是一个三进制的问题。记得以前我探讨过一个称坏乒乓球的问题,和这个问题有异曲同工之处。
小曹的思路是对的,但答案[log3(n)]+1是不对的。
下面我简单分析一下这个问题。
这个问题其实包含三个子问题:
1.称量需要多少个砝码。
2.每个砝码的质量是多少。
3.称量某一重物,达到平衡状态时,每个砝码如何摆放。
先讨论第1个问题。
不管是天平还是普通杆秤,砝码的放置位置都有三个,一是不放在秤上,二是放在秤上与被称物不在一起,三是放在秤上与被称物在一起。楼主的“标准答案”并没有考虑第三种情况。
我们还是以天平作为衡器来描述问题吧。
我们可以设被称量物总是放在左边的,这样天平的左右盘的意义就不同了。
每个砝码在称量中可以有三种状态。放在左边、不放上天平、放在右边。
那么N个砝码最多可以组合出3^N种状态。这3^N种状态除了全不放上天平外其它状态都有一个对称的状态。
假设有某一状态是左边放了Sa砝码,右边放了Sb砝码(Sa,Sb都是砝码集S的子集,并且Sa、Sb不相交),那么必然会有另一状态(左边放Sb右边放Sa)与之对应。
设左边砝码总重量为Wa,右边为Wb,则这一平衡状态下的被称物重量为Wb - Wa。
与它对称的状态在平衡时被称物重量为Wa - Wb。
很显然,这一对状态下被称物重量必然有一个为负值。负重量在现实中不存在。
所以N个砝码能表示的正状态为(3^N - 1)/2种。也就是能表示的最大的被称物质量。
由此,称量重量为W的重物时需要的砝码数是 N = log3(2 * W + 1)。这是小曹说错的地方。
接下来讨论第2个问题。
有了上面的讨论其实这第2个问题的结论已经出来了。状态呈现三进制计数的性质,所以每个砝码3的幂次,这样即可保证状态无重叠且连续。
从小到大,第i个砝码的质量应该是 3^(i - 1)。
最后讨论第3个问题。
将状态表示成三进制数,每一位对应一个砝码(质量为该位的基)的取值为0、1、2。现在我们建立三进制数与被称量物重量的对应关系。
我们规定0表示该位对应砝码放在左边,1表示不放在天平上,2表示放在右边。
这么规定是有意义的。
我们希望用三进制数的变化情况与被称物质量的变化对应。也就是说被称物质量越大这个三进制数的值也越大。
在这种规定下三进制0的意义是什么?
是所有砝码都放在左边,即与被称量物放在一起。这时如果要天平平衡,那被称量物具有的将是负质量。
而天平上什么都不放的状态(零质量)对应的数值是...1111111。即每位都是1。
所以这里只是应用了部分的三进制计数性质,而非正常意义下的三进制数。
这里的0表示的其实是负无穷,而无限多的1表示的是十进制数0。它其实是将整个整数域映射到了非负整数域。
而我们使用的是有限位三进制数,且最高位为2(略去更高位的无限个1)。
这个概念比较抽象,如果理解不了可以跳过。
由于每位为1表示零质量,而我们用N个砝码。所以我们计算的基准值应为(N^3 - 1) / 2。
对于重量为W的重物,它对应的三进制数值为。
(3^N - 1) / 2 + W
这个三进制值的每一位的值将表示该位对应砝码的放置位置。
结尾,送一段代码来输出上面理论的计算结果,供大家验证。
程序代码:
#include<stdio.h>
#include<math.h>
int f(int w)
{
return ceil(log(w * 2 + 1) / log(3));
}
void print_weight(int w)
{
int n, a;
const char * B[] = {"L", "O", "R"};
n = f(w);
for(a = (int)(pow(3, n) - 1) / 2 + w; a; a /= 3)
printf("%5s ", B[a % 3]);
}
int main()
{
int w, n, i, t;
printf("Input weight : ");
scanf("%d", &w);
n = f(w);
printf("It needs %d weights.\n", n);
printf("Every weights is:\n");
for(i = 0, t = 1; i < n; i++, t *= 3) printf("%5d ", t);
puts("");
print_weight(w);
return 0;
}