问题描述
题目:
林克有12枚银币。其中有11枚真币和1枚假币。假币看起来和真币没有区别,但是重量不同。但林克不知道假币比真币轻还是重。
于是他向他朋友约珥借了一架天平,用这架天平称了这些币三次。
如果用天平称两枚硬币,发现天平平衡,说明两枚都是真的。如果用一枚真币与另一枚银币比较,发现它比真币轻或重,说明它是假币。
经过精心的设计,聪明的林克根据这三次称量结果找出假币,并且能够确定假币是轻是重。
如果给你林克的称量数据,你也可以找出假币并且确定假币是轻是重吗?(林克提供的称量数据保证一定能找出假币)。
输入:
第一行有一个数字n,表示有n组测试用例。
对于每组测试用例:
输入有三行,每行表示一次称量的结果。林克事先将银币标号为A-L。
每次称量的结果用三个以空格隔开的字符串表示:
天平左边放置的硬币 天平右边放置的硬币 平衡状态。
其中平衡状态用``up'', ``down'', 或 ``even''表示, 分别为右端高、右端低和平衡。天平左右的硬币数总是相等的。
样例:
1
ABCD EFGH even
ABCI EFJK up
ABIJ EFGH even
输出:
输出哪一个标号的银币是假币,并说明它比真币轻还是重(heavy or light)。
样例:
K is the counterfeit coin and it is light.
思路
分析所给输入的特征可知:
- 每次天平两边的硬币数必然相等。
- 假币必然在被称量的硬币之中。(从未被称过的硬币的数目必然为偶数,无法使用排除法)
- 如果天平平衡,则两边的硬币都是真币。
- 如果某一枚硬币既在某一次出现在天平轻的一端,又在另一次出现在天平重的那一端,则这枚硬币必为真币。
- 如果某一枚硬币始终出现在重的一端,则这枚硬币不能确定为假币或真币,但重复出现次数多的硬币为假币的可能性更大(只要存在出现次数更多的硬币,只出现在同一侧次数少的硬币就不可能是假币)。
- 通过三次称量结果一定能找出假币,即不存在多枚硬币都不能确定真假。
代码设计思路:
先用STL容器存储天平左右的硬币,再根据天平最终状态对硬币进行标记,我用1表示硬币所在天平平衡(一定为真币),用-1表示该硬币有一次在天平轻的一端,-2为有两次,以此类推,用2表示该硬币有一次在天平重的一端,4为有两次,以此类推。
容易想到利用map的键值对来存储硬币和标记,因为map的key值不会重复。此外每次标记前还需要根据硬币状态修改标记,若硬币本次在轻(重)的一端,而硬币状态显示硬币曾经在重(轻)的一端,由上文分析的第四点知硬币必为真币,应修改为1;若本次天平平衡,则把两侧所有硬币都标记为1;若硬币状态和本次在天平上的状态相同,则统计次数加一。
当三次标记都完成后,将不能确定真假的硬币输出,由上文分析第六点知,此硬币必然唯一。
代码实现
主函数:
int main(){
int n;//输入测试数据的组数
cin>>n;
for(int i=0;i<n;i++){
map<char,int> keep;//用map保存硬币和对应的标记
vector<char> left;//保存天平左边的硬币
vector<char> right;//保存天平右边的硬币
for(int j=0;j<3;j++){//三次测量
coinread(left,right);//硬币读取的函数
string flag;//存储天平的状态
cin>>flag;
coinmark(keep,left,right,flag);//修改硬币标记的函数
left.clear();
right.clear();//清空保存的硬币,为存储下一次测量数据做准备
}
coinprint(keep);//输出不能确定真假的硬币,由分析知,必为假币
}
}
coinread读取硬币的函数:
void coinread(vector<char>&left,vector<char>&right){
char t;
cin>>t;//读取第一个非空字符
while(t!=' '){//直到读到空格停止
if(t>='A'&&t<='L')//一共十二枚硬币
left.push_back(t);
t=getchar();//不用cin是为了防止跳过空格
}
cin>>t;
while(t!=' '){//同上,不再赘述
if(t>='A'&&t<='L')
right.push_back(t);
t=getchar();
}
}
coinmark修改硬币标记的函数:
void coinmark(map<char,int>& keep,vector<char> &left,vector<char>&right,string &flag){
if(flag=="even"){//天平平衡,把所有硬币标记为1
for(int a=0;a<left.size();a++){
keep[left[a]]=1;
}
for(int a=0;a<right.size();a++){
keep[right[a]]=1;
}
}
else if(flag=="down"){//天平右端向下,右端重,左端轻
for(int a=0;a<left.size();a++){
if(keep[left[a]]==2||keep[left[a]]==4)//如果左端的硬币曾经在重的一端,则标记为1
keep[left[a]]=1;
else if(keep[left[a]]!=1)//否则统计次数,因为是轻的一端,-1(map会初始化value为0)
keep[left[a]]-=1;
}
for(int a=0;a<right.size();a++){//以下分情况讨论都类似,不再赘述
if(keep[right[a]]==-1||keep[right[a]]==-2)//枚举到两次是因为一共只测三次
keep[right[a]]=1;
else if(keep[right[a]]!=1)
keep[right[a]]+=2;
}
}
else{天平右端向上,左端重,右端轻
for(int a=0;a<left.size();a++){
if(keep[left[a]]==-1||keep[left[a]]==-2)
keep[left[a]]=1;
else if(keep[left[a]]!=1)
keep[left[a]]+=2;
}
for(int a=0;a<right.size();a++){
if(keep[right[a]]==2||keep[right[a]]==4)
keep[right[a]]=1;
else if(keep[right[a]]!=1)
keep[right[a]]-=1;
}
}
}
coinprint输出不确定真假的硬币的函数:
void coinprint(map<char,int>& keep){
for(map<char,int>::iterator k=keep.begin();k!=keep.end();k++){
if(k->second==-3){//从在同一侧次数多的硬币开始寻找,找到就return
cout<<k->first<<" is the counterfeit coin and it is light. "<<endl;
return;//因为如果存在同一端三次的硬币,则只在同一端出现过两次的硬币就不可能是假币
}
if(k->second==6){
cout<<k->first<<" is the counterfeit coin and it is heavy. "<<endl;
return;
}
}
for(map<char,int>::iterator k=keep.begin();k!=keep.end();k++){
if(k->second==-2){
cout<<k->first<<" is the counterfeit coin and it is light. "<<endl;
return;
}
if(k->second==4){
cout<<k->first<<" is the counterfeit coin and it is heavy. "<<endl;
return;
}
}
for(map<char,int>::iterator k=keep.begin();k!=keep.end();k++){
if(k->second==-1){
cout<<k->first<<" is the counterfeit coin and it is light. "<<endl;
return;
}
if(k->second==2){
cout<<k->first<<" is the counterfeit coin and it is heavy. "<<endl;
return;
}
}
}
完整代码(c++)
#include<iostream>
#include<map>
#include<vector>
#include<string>
using namespace std;
void coinread(vector<char>&left,vector<char>&right){
char t;
cin>>t;
while(t!=' '){
if(t>='A'&&t<='L')
left.push_back(t);
t=getchar();
}
cin>>t;
while(t!=' '){
if(t>='A'&&t<='L')
right.push_back(t);
t=getchar();
}
}
void coinmark(map<char,int>& keep,vector<char> &left,vector<char>&right,string &flag){
if(flag=="even"){
for(int a=0;a<left.size();a++){
keep[left[a]]=1;
}
for(int a=0;a<right.size();a++){
keep[right[a]]=1;
}
}
else if(flag=="down"){
for(int a=0;a<left.size();a++){
if(keep[left[a]]==2||keep[left[a]]==4)
keep[left[a]]=1;
else if(keep[left[a]]!=1)
keep[left[a]]-=1;
}
for(int a=0;a<right.size();a++){
if(keep[right[a]]==-1||keep[right[a]]==-2)
keep[right[a]]=1;
else if(keep[right[a]]!=1)
keep[right[a]]+=2;
}
}
else{
for(int a=0;a<left.size();a++){
if(keep[left[a]]==-1||keep[left[a]]==-2)
keep[left[a]]=1;
else if(keep[left[a]]!=1)
keep[left[a]]+=2;
}
for(int a=0;a<right.size();a++){
if(keep[right[a]]==2||keep[right[a]]==4)
keep[right[a]]=1;
else if(keep[right[a]]!=1)
keep[right[a]]-=1;
}
}
}
void coinprint(map<char,int>& keep){
for(map<char,int>::iterator k=keep.begin();k!=keep.end();k++){
if(k->second==-3){
cout<<k->first<<" is the counterfeit coin and it is light. "<<endl;
return;
}
if(k->second==6){
cout<<k->first<<" is the counterfeit coin and it is heavy. "<<endl;
return;
}
}
for(map<char,int>::iterator k=keep.begin();k!=keep.end();k++){
if(k->second==-2){
cout<<k->first<<" is the counterfeit coin and it is light. "<<endl;
return;
}
if(k->second==4){
cout<<k->first<<" is the counterfeit coin and it is heavy. "<<endl;
return;
}
}
for(map<char,int>::iterator k=keep.begin();k!=keep.end();k++){
if(k->second==-1){
cout<<k->first<<" is the counterfeit coin and it is light. "<<endl;
return;
}
if(k->second==2){
cout<<k->first<<" is the counterfeit coin and it is heavy. "<<endl;
return;
}
}
}
int main(){
int n;
cin>>n;
for(int i=0;i<n;i++){
map<char,int> keep;
vector<char> left;
vector<char> right;
for(int j=0;j<3;j++){
coinread(left,right);
string flag;
cin>>flag;
coinmark(keep,left,right,flag);
left.clear();
right.clear();
}
coinprint(keep);
}
}
可通过评测。
完整代码(python)
def coinread():
left=[]
right=[]
str=list(input().split(' '))
for i in str[0]:
left.append(i)
for i in str[1]:
right.append(i)
flag=''.join(str[2])
return left,right,flag
def coinmark(keep,left,right,flag):
if flag=="even":
for a in left:
keep[a]=1
for a in right:
keep[a]=1
elif flag=="down":
for a in left:
if a not in keep:
keep[a]=0
if keep[a]==2 or keep[a]==4:
keep[a]=1
elif keep[a] !=1:
keep[a]-=1
for a in right:
if a not in keep:
keep[a]=0
if keep[a]==-1 or keep[a]==-2:
keep[a]=1
elif keep[a]!=1:
keep[a]+=2
else:
for a in left:
if a not in keep:
keep[a]=0
if keep[a]==-1 or keep[a]==-2:
keep[a]=1
elif keep[a]!=1:
keep[a]+=2
for a in right:
if a not in keep:
keep[a]=0
if keep[a]==2 or keep[a]==4:
keep[a]=1
elif keep[a]!=1:
keep[a]-=1
return keep
def coinprint(keep):
for key, value in keep.items():
if value==-3:
print(key,end='')
print(" is the counterfeit coin and it is light. ")
return
if value==6:
print(key,end='')
print(" is the counterfeit coin and it is heavy. ")
return
for key, value in keep.items():
if value==-2:
print(key,end='')
print(" is the counterfeit coin and it is light. ")
return
if value==4:
print(key,end='')
print(" is the counterfeit coin and it is heavy. ")
return
for key, value in keep.items():
if value==-1:
print(key,end='')
print(" is the counterfeit coin and it is light. ")
return
if value==2:
print(key,end='')
print(" is the counterfeit coin and it is heavy. ")
return
n=int(input())
for i in range(n):
keep=dict()
left=[]
right=[]
flag=''
for j in range(3):
left,right,flag=coinread()
keep=coinmark(keep,left,right,flag)
coinprint(keep)
可通过评测。