基础算法之二——枚举法“赛利的硬币”
题目描述
赛利有 12枚银币。其中有 11枚真币和1枚假币。假币看起来和真币没有区别,但是重量不同。但赛利不知道假币比真币轻还是重。于是他向朋友借了一架天平。朋友希望赛利称三次就能找出假币并且确定假币是轻是重。例如:如果赛利用天平称两枚硬币,发现天平平衡,说明两枚都是真的。如果赛利用一枚真币与另一枚银币比较,发现它比真币轻或重,说明它是假币。经过精心安排每次的称量,赛利保证在称三次后确定假币。
输入数据
输入有三行,每行表示一次称量的结果。赛利事先将银币标号为A-L。每次称量的结果用三个以空格隔开的字符串表示:天平左边放置的硬币天平右边放置的硬币平衡状态。其中平衡状态用"up‘‘, "down‘‘,或 "even‘‘表示, 分别为右端高、右端低和平衡。天平左右的硬币数总是相等的。
输出要求
输出哪一个标号的银币是假币,并说明它比真币轻还是重。
输入样例
1
ABCD EFGH even
ABCI EFJK up
ABIJ EFGH even
输出样例
K is the counterfeit coin and it is light.
心得
对这道题目的心得主要不是在于算法,而是在于如何将数据转化为计算机的内部表示,又怎么利用计算机的内部表示进行程序逻辑所需要做的计算处理。
1、如何存储输入的多行字符数据(用二维字符数组)
2、存储的数据不方便直接参与计算,如何转化才方便计算?(cLeft转化为iLeft,cRight转化为iRight)
3、比较两边的重量,如何用计算机能够表示的方式去实现?(iLeft iRight整型数组实现加减比较)
4、如何实现枚举?(weight数组存放每一枚硬币的信息,逐个列出可能的情况)
5、小技巧(做循环或者条件判断的边界,cLeft cRight的设置多了一位,记录串的长度以便于控制循环)
解题思路:C++语言描述
#include<iostream>
using namespace std;
int main()
{
//
int iLeft[3][6],iRight[3][6],iResult[3];
//存放三个左边条件的长度和三个右边条件的长度
int LeftLen[3],RightLen[3];
//把输入的数据转入为内部存储表示
char cLeft[3][7],cRight[3][7],cResult[3][5];
/*
将ABCD EFGH even看成三个串,用三个字符数组去存,那么程序内部就需要3个字符数组。如下:
char cLeft[7],cRight[7],cResult[5];
为什么是7,是因为最后一个位char[6]有其他用处,而前六位[0]~[5]刚好是12枚硬币最多每组可以分6个币。
因为每一次测量都有三个条件,所以3个字符数组这时需要使用三个二维数组表示,此时演变为:
char cLeft[3][7],cRight[3][7],cResult[3][5];
cLeft[3][7]:存储输入条件的第一项,左边的硬币组合
cRight[3][7]:存储输入条件的第二项,右边的硬币组合
cResult[3][5]存储输入条件中的第三项“判断结果”
*/
int n;
cin>>n;
while(n--){
//初始化一个数组:每个位表示1~12枚硬币哪一枚偏重、偏轻或者属于正常,枚举的时候使用
int weight[12]={};
for(int i=0;i<3;i++){
//初始化这三个cLeft[3][7],cRight[3][7],cResult[3][5]二维数组时,默认元素全都是0
cin>>cLeft[i]>>cRight[i]>>cResult[i];
for(int j=0;;j++)
//记录第i个条件的左边数的长度
if(cLeft[i][j]==0){
LeftLen[i]=j;
break;
}
for(int j=0;;j++)
//记录第i个条件的右边数的长度
if(cRight[i][j]==0){
RightLen[i]=j;
break;
}
//result数组保存三个条件的结果,0表示相等,1表示左边比右边重,-1表示左边比右边轻
if(cResult[i][0]==‘e‘)
iResult[i]=0;
else if(cResult[i][0]==‘u‘)
iResult[i]=-1;
else iResult[i]=1;
//记录左右两边是具体什么数
for(int j=0;j<LeftLen[i];j++)
//iLeft存放左边字符串每个字符的数字表示,内部约定A=0 ~ L=11
iLeft[i][j]=cLeft[i][j]-‘A‘;
for(int j=0;j<RightLen[i];j++)
//iRight存放左边字符串每个字符的数字表示,内部约定A=0 ~ L=11
iRight[i][j]=cRight[i][j]-‘A‘;
}
bool found=false;
//开始枚举:从第一个币weight[0]到最后一个币weight[11]
for(int i=0;i<12&&!found;i++){
//表示已经找到该硬币
found=true;
//开始枚举,如果第i个是1,则对应的是该枚是假币且更重的情况
weight[i]=1;
//一共三个条件,当找到了三个条件都符合的情况
for(int j=0;j<3;j++){
int rightWeight=0,leftWeight=0;
for(int k=0;k<RightLen[j];k++)
//循环右边字符串数组,每个数叠加
rightWeight+=weight[iRight[j][k]];
for(int k=0;k<LeftLen[j];k++)
//循环右边字符串数组,每个数叠加
leftWeight+=weight[iLeft[j][k]];
if(rightWeight-leftWeight!=iResult[j]){
//判断是不是符合给定的结果
found=false;
break;
}
}
if(found){
char coin;
coin=‘A‘+i;
cout<<coin<<" is the counterfeit coin and it is heavy."<<endl;
break;
}
found=true;
//开始枚举,如果第i个是1,则对应的是该枚是假币且更轻的情况
weight[i]=-1;
//一共三个条件,当找到了三个条件都符合的情况
for(int j=0;j<3;j++){
int rightWeight=0,leftWeight=0;
for(int k=0;k<RightLen[j];k++)
//循环右边字符串数组,每个数叠加
rightWeight+=weight[iRight[j][k]];
for(int k=0;k<LeftLen[j];k++)
//循环右边字符串数组,每个数叠加
leftWeight+=weight[iLeft[j][k]];
if(rightWeight-leftWeight!=iResult[j]){
//判断是不是符合给定的结果
found=false;
break;
}
}
if(found){
char coin;
coin=‘A‘+i;
cout<<coin<<" is the counterfeit coin and it is light."<<endl;
break;
}
weight[i]=0;
}
}
return 0;
}