- 论坛徽章:
- 0
|
2.7 天 平 称 物
1.问题描述
有4个砝码,总重量是40克,砝码的质量是整数,且各不相等。请确定它们的质量,使之能称出1~40克任何整数质量的物体。
2.问题分析
这是问题可以采用穷举的办法来实现。假设4块砝码的重量分别为w1、w2、w3、w4,只要穷举出这样的组合{w1、w2、w3、w4},其中,w1、w2、w3、w4互不相等,w1+w2+w3+w4=40,那么组合{w1、w2、w3、w4}就可能是该问题的解。如果这个组合可以称出1~40克的任意质量,那么这个组合就是问题的答案。
(1)确定程序框架
通过前面的问题分析,主要要分两个步骤:首先如何穷举出所有的组合{w1、w2、w3、w4},其中w1、w2、w3、w4互不相等,w1+w2+w3+w4=40。其次,如何判断组合{w1、w2、w3、w4}是否可以称出1~40克的任意质量。为了看得更加清楚,我们可以把称重的结果打印出来。程序的框架如下:
public static void main(String args[])
{
//穷举列出这种组合
//判断当前组合是否可以称出1~40克的任意质量
//打印出称重结果
}
(2)穷举列出各种组合
可以采用多重嵌套的方式实现穷举,为了减少穷举次数,我们可以让内层循环搜索的范围小于外层循环搜索的范围。这样每次得到的组合方式的4个元素都不会相等,同时也不会出现“相同组合,不同排列”的情况。关键代码如下:
for(weight1=1;weight1<=40;weight1++)
for(weight2=weight1+1;weight2<=40-weight1;weight2++)
//注意:内循环起始值为外循环值加1
for(weight3=weight2+1;weight3<=40-weight1-weight2;weight3++)
if((weight4=40-weight1-weight2-weight3)>=weight3)
{
//验证当前组合是否可以称出1~40克的任意质量
}
(3)判断当前组合是否可以称出1~40克的任意质量
接下来的问题就是如何判断组合{w1、w2、w3、w4}是否可以称出1~40克的任意质量。如果将其用数学表达式写出,就是要判断方程:
a1w1+a2w2+a3w3+a4w4=W a1,a2,a3,a4∈–1,0,1
在W=1,2,3,…,40时是否都有解。注意,该方程的解a1、a2、a3、a4只能从中{–1,0,1}取值。假设存在这样一组解{a1, a2, a3, a4}={1, –1, 0, 1}使得方程:
a1w1+a2w2+a3w3+a4w4=7
有解,则有:
1*w1+(–1)*w2+0*w3+1*w4=7
=>w1+w4=W+w2
这样天平的一边放上砝码w1和w4,另一边放上砝码w2和重为7克的物品,这说明砝码{w1、w2、w3、w4}可以称出7克的质量。同理,如果方程:
a1w1+a2w2+a3w3+a4w4=W a1,a2,a3,a4∈–1,0,1
在W=1,2,3,…,40时都有解,那么就说明砝码{w1、w2、w3、w4}组合可以称出1~40克的任意质量。关键代码如下:
Int flag=0; //flag为1代表组合找到了,为0代表组合没找到
for(flag=1,x=1;x<41&&flag==1;x++)
//重物在天平左面,d的各种状态包括:-1:砝码在天平左面;1:砝码在右面;0:不用该砝码
for(flag=0,d1=1;d1>-2;d1--)
for(d2=1;d2>-2&&flag==0;d2--)
for(d3=1;d3>-2&&flag==0;d3--)
for(d4=1;d4>-2&&flag==0;d4--)
if(x==weight1*d1+weight2*d2+weight3*d3+weight4*d4)
flag=1;
(4)打印称重结果
砝码的组合找到之后,下面就通过这个组合把如何称重显示出来,过程分析同第(3)步,关键代码如下:
String left = "",right="";
//a1a2a3a4
for(int i=1;i<=40;i++) //循环40次,打印出每种情况
for(int a1=-1;a1<=1;a1++) //三种状态都循环一遍
for(int a2=-1;a2<=1;a2++)
for(int a3=-1;a3<=1;a3++)
for(int a4=-1;a4<=1;a4++)
{
if(i==a1*w4+a2*w3+a3*w2+a4*w1) //如果相等,打印出结果
{
boolean f=true; //表达式右侧第一项标志
left=i+"";
switch (a1) //根据状态,修改表达式
{
case -1: //状态-1:左侧,修改左侧表达式
left=left+"+"+w4;
break;
case 1: //状态1:右侧,修改右侧表达式
if(f) //如果是右侧第一项,数值前不用加“+”
right=right+w4;
Else //如果不是右侧第一项,数值前加“+”
right=right+"+"+w4;
f=false; //右侧出现过数值,修改f标志位
break;
}
switch (a2) //根据状态,修改表达式
{
case -1: //状态-1:左侧,修改左侧表达式
left=left+"+"+w3;
break;
case 1: //状态1:右侧,修改右侧表达式
if(f) //如果是右侧第一项,数值前不用加“+”
right=right+w3;
else
right=right+"+"+w3;
//如果不是右侧第一项,数值前加“+”
f=false; //右侧出现过数值,修改f标志位
break;
}
switch (a3) //根据状态,修改表达式
{
case -1: //状态-1:左侧,修改左侧表达式
left=left+"+"+w2;
break;
case 1: //状态1:右侧,修改右侧表达式
if(f)
right=right+w2;
else
right=right+"+"+w2;
f=false; //右侧出现过数值,修改f标志位
break;
}
switch (a4) //根据状态,修改表达式
{
case -1: //状态-1:左侧,修改左侧表达式
left=left+"+"+w1;
break;
case 1: //状态1:右侧,修改右侧表达式
if(f)
right=right+w1;
else
right=right+"+"+w1;
f=false; //右侧出现过数值,修改f标志位
break;
}
System.out.println(left+"="+right);
}
left=""; //下次循环,清空
right="";
(5)完整程序
现在我们就需要把刚才的程序进行组合,构成我们的完整程序:
public class ch2_7_3
{
/**
* @param args
*/
public static void main(String[] args)
{
int weight1,weight2,weight3,weight4,d1,d2,d3,d4,x,flag;
int w1=0,w2=0,w3=0,w4=0;
System.out.printf("The weight is broke up as following 4 pieces:");
for(weight1=1;weight1<=40;weight1++) //穷举
for(weight2=weight1+1;weight2<=40-weight1;weight2++)
//注意:内循环起始值为外循环值加1
for(weight3=weight2+1;weight3<=40-weight1-weight2; weight3++)
if((weight4=40-weight1-weight2-weight3)>=weight3)
{
for(flag=1,x=1;x<41&&flag==1;x++)
//重物在天平左面,d的各种状态包括:-1:砝码在天平左面;
1:砝码在右面;0:不用该砝码
for(flag=0,d1=1;d1>-2;d1--) //三种状态都循环一遍
for(d2=1;d2>-2&&flag==0;d2--)
for(d3=1;d3>-2&&flag==0;d3--)
for(d4=1;d4>-2&&flag==0;d4--)
//找到组合,修改标志
if(x==weight1*d1+weight2*d2+ weight3*d3+weight4*d4)
flag=1;
if(flag==1)//flag为1代表组合找到了,为0代表组合没找到
{
System.out.printf("%d %d %d %dn",weight1, weight2,weight3,weight4);
w1=weight1; //找到组合,为下一步输出做准备
w2=weight2;
w3=weight3;
w4=weight4;
}
}
// System.out.printf("n%d %d %d %dn",w1,w2,w3,w4);
String left = "",right=""; //表达式初始化
//a1a2a3a4
for(int i=1;i<=40;i++) //循环40次,打印出每种情况
for(int a1=-1;a1<=1;a1++) //三种状态都循环一遍
for(int a2=-1;a2<=1;a2++)
for(int a3=-1;a3<=1;a3++)
for(int a4=-1;a4<=1;a4++)
{
if(i==a1*w4+a2*w3+a3*w2+a4*w1)
//如果相等,打印出结果
{
boolean f=true; //表达式右侧第一项标志
left=i+"";
switch (a1){ //根据状态,修改表达式
case -1://状态-1:左侧,修改左侧表达式
left=left+"+"+w4;
break;
case 1: //状态1:右侧,修改右侧表达式
if(f) //如果是右侧第一项,数值前 不用加“+”
right=right+w4;
else //如果不是右侧第一项,数值 前加“+”
right=right+"+"+w4;
f=false;
//右侧出现过数值,修改f标志位
break;
}
switch (a2){ //根据状态,修改表达式
case -1: //状态-1:左侧,修改左侧表达式
left=left+"+"+w3;
break;
case 1: //状态1:右侧,修改右侧表达式
// right=right+"+"+w3;
if(f) //如果是右侧第一项,数值前不用 加“+”
right=right+w3;
else
right=right+"+"+w3;
f=false;//右侧出现过数值,修改f标志位
break;
}
switch (a3){//根据状态,修改表达式
case -1: //状态-1:左侧,修改左侧表达式
left=left+"+"+w2;
break;
case 1: //状态1:右侧,修改右侧表达式
if(f) //如果是右侧第一项,数值前不用 加“+”
right=right+w2;
else
right=right+"+"+w2;
f=false;//右侧出现过数值,修改f标志位
break;
}
switch (a4){//根据状态,修改表达式
case -1: //状态-1:左侧,修改左侧表达式
left=left+"+"+w1;
break;
case 1: //状态1:右侧,修改右侧表达式
if(f) //如果是右侧第一项,数值前不用 加“+”
right=right+w1;
else
right=right+"+"+w1;
f=false;//右侧出现过数值,修改f标志位
break;
}
System.out.println(left+"="+right);
//输出表达式
}
left=""; //下次循环,清空
right="";
}
}
}
|
|