题目设定有重量为1, 3, 9, 27,...,3^19的20个砝码和一个天平,给一个物体放在天平左边,如何在天平的左边与右边添加合适的砝码,使天平两边平衡,从而求出物体的重量。注意20个砝码每个最多只能用一次。
题解1:定义问题:将重量为W的物体放在天平左边,找出合适的砝码放在天平右边(必要时在左边也加上若干玛法),使天平平衡。下面叙述我对该问题的想法:
1)从玛法中找到不小于W的重量Mn(Mn为1, 3, 9, 27,...,3^19中的一个数)。易算出小于M的所有重量之和 1 + 3 + 9 + ... + Mn-1 = (Mn - 1) / 2。
2)若 W > (Mn - 1) / 2,则在天平右边放上砝码Mn,这时可以将问题转化为将重量为 W = Mn - W 的物体放在天平右边,找出合适的砝码放在天平的左边(必要时在右边也加上若干玛法),使天平平衡。跳至步骤1,从而递归解决该问题。
3)否则,在天平右边放上玛法Mn-1,则W = W - Mn-1,跳至步骤1。
根据上述思想写成的C代码,结果正确,但是却超时了。。。有机会再优化之。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int binsearch(int poises[], int low, int high, int object)//二分查找重量不小于object的砝码
{
int found, mid;
found = 0;
while (!found && low <= high)
{
mid = (low + high) / 2;
if (poises[mid] == object)
found = 1;
else if (poises[mid] > object)
high = mid - 1;
else
low = mid + 1;
}
if (found)
return mid;
else
return low;//返回比target略大的数
}
int main()
{
int i, j, testnum, poises[20], object; // poises[20]:砝码重量, object:物体重量
char flag, lcount, rcount, left[20], right[20];
j = 1;
for (i = 0; i < 20; ++i)
{
poises[i] = j;
j *= 3;
}
scanf("%d", &testnum);
while (testnum--)
{
scanf("%d", &object);
i = binsearch(poises, 0, 19, object);
memset(left, 0, i + 1);
memset(right, 0, i + 1);
lcount = 0;
rcount = 0;
j = i;
flag = 1;
while (object)
{
if (object <= (poises[j] - 1) / 2) //此时按步骤2进行
{
--j;
object -= poises[j];
if (flag)
{
right[j] = 1;
++rcount;
}
else
{
left[j] = 1;
++lcount;
}
}
else //此时按步骤1进行
{
object = poises[j] - object;
if (flag)
{
right[j] = 1;
++rcount;
}
else
{
left[j] = 1;
++lcount;
}
flag = 1 - flag;
}
j = binsearch(poises, 0, j, object);
}
// 打印结果
if (lcount)
{
j = 0;
while (!left[j])
++j;
printf("%d", poises[j]);
for (++j; j <= i; ++j)
if (left[j])
printf(",%d", poises[j]);
}
else
printf("empty");
printf(" ");
j = 0;
while (!right[j])
++j;
printf("%d", poises[j]);
for (++j; j <= i; ++j)
if (right[j])
printf(",%d", poises[j]);
printf("\n");
}
return 0;
}
题解2:根据网上的资料,采用平衡三进制思想解题。首先将W表示为三进制的形式anan-1...ak...a1a0,此时ak必为0,1,2之一。
然后从低位到高位,若 ak 为2,则将 ak 改写成-1,同时向高一位进1;若 ak 为3(由于有低位的进位),则将 ak 改写成0,同时向高一位进1。此时W就写成了平衡三进制形式(即用-1,0,1表示的三进制数),如当W为十进制的8时:(8)10 = (22)3 =(3,-1)3 = (1,0,-1)3。
最后将数码 ak 为-1的数移到等号左边来,就得到了W的在天平左边中平衡时的砝码情况表示,如W为8时,W = -3^0 + 3^2,将-1项移动左边得W + 1 = 9,因此输出的答案为“1 9”。
AC的C代码:
#include <stdio.h>
int main()
{
int i, j, testnum, poises[20], object;
char count, tribit[21];
j = 1;
for (i = 0; i < 20; ++i)
{
poises[i] = j;
j *= 3;
}
scanf("%d", &testnum);
while (testnum--)
{
scanf("%d", &object);
for (i = 19; i >= 0; --i) //转换成三进制
{
tribit[i] = object / poises[i];
object %= poises[i];
}
count = 0;
for (i = 0; i < 20; ++i) //进一步转换成平衡三进制
if (tribit[i] == 2)
{
tribit[i] = -1;
++count;
tribit[i + 1] += 1;
}
else if (tribit[i] == 3)
{
tribit[i] = 0;
tribit[i + 1] += 1;
}
//打印结果
if (count)
{
i = 0;
while (tribit[i] != -1)
++i;
printf("%d", poises[i]);
for (++i; i < 21; ++i)
if (tribit[i] == -1)
printf(",%d", poises[i]);
}
else
printf("empty");
printf(" ");
i = 0;
while (tribit[i] != 1)
++i;
printf("%d", poises[i]);
for (++i; i < 21; ++i)
if (tribit[i] == 1)
printf(",%d", poises[i]);
printf("\n");
}
return 0;
}