xmuoj假币问题非枚举思路题解

问题描述

题目:

林克有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. 

思路

分析所给输入的特征可知:

  1. 每次天平两边的硬币数必然相等。
  2. 假币必然在被称量的硬币之中。(从未被称过的硬币的数目必然为偶数,无法使用排除法) 
  3. 如果天平平衡,则两边的硬币都是真币。
  4. 如果某一枚硬币既在某一次出现在天平轻的一端,又在另一次出现在天平重的那一端,则这枚硬币必为真币。
  5. 如果某一枚硬币始终出现在重的一端,则这枚硬币不能确定为假币或真币,但重复出现次数多的硬币为假币的可能性更大(只要存在出现次数更多的硬币,只出现在同一侧次数少的硬币就不可能是假币)。
  6. 通过三次称量结果一定能找出假币,即不存在多枚硬币都不能确定真假。

代码设计思路: 

先用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)

可通过评测。

  • 8
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值