OpenJ_Bailian - 2692:假币问题(枚举/暴力)

题目链接
描述

  • 赛利有12枚银币。其中有11枚真币和1枚假币。假币看起来和真币没有区别,但是重量不同。但赛利不知道假币比真币轻还是重。于是他向朋友借了一架天平。朋友希望赛利称三次就能找出假币并且确定假币是轻是重。例如:如果赛利用天平称两枚硬币,发现天平平衡,说明两枚都是真的。如果赛利用一枚真币与另一枚银币比较,发现它比真币轻或重,说明它是假币。经过精心安排每次的称量,赛利保证在称三次后确定假币。

输入

  • 第一行有一个数字n,表示有n组测试用例。
    对于每组测试用例:
    输入有三行,每行表示一次称量的结果。赛利事先将银币标号为A-L。每次称量的结果用三个以空格隔开的字符串表示:天平左边放置的硬币 天平右边放置的硬币 平衡状态。其中平衡状态用”up’’, “down’’, 或 “even’'表示, 分别为右端高、右端低和平衡。天平左右的硬币数总是相等的。

输出

  • 输出哪一个标号的银币是假币,并说明它比真币轻还是重(heavy or light)。

样例输入

1
ABCD EFGH even 
ABCI EFJK up 
ABIJ EFGH even

样例输出

K is the counterfeit coin and it is light.

一个好用的调试样例
样例来源

6
ABCD EFGH even
IJ KA up
JK AB up
ABCD EFGH even
IJ KA up
JK AB down
ABCD EFGH even
IJ KA up
JK AB even
ABCD EFGH even
IJ KA down
JK AB up
ABCD EFGH even
IJ KA down
JK AB down
ABCD EFGH even
IJ KA down
JK AB even

输出结果

J is the counterfeit coin and it is heavy.
K is the counterfeit coin and it is light.
I is the counterfeit coin and it is heavy.
K is the counterfeit coin and it is heavy.
J is the counterfeit coin and it is light.
I is the counterfeit coin and it is light.

枚举

既然题目保证能将假币甄别出来,那么,我们只需要枚举所有硬币,假设第 i 枚硬币为假币,再将之通过所输入的三次测量中,判断若该硬币为假币,三次称量是否成立,且仅12枚硬币及三次称量结果保证了该枚举的时间复杂度符合条件。

需要注意的就是称量的时候,不一定一次称量四枚硬币,在一次称量中,天平一端的硬币数量为[1,6]。

当检验的硬币符合以下三个条件之一时,可确定其为真币:

  • 受检验硬币未参与某次称量时,该次称量天平不平衡。
  • 受检验硬币参与某次称量时,该次称量天平平衡。
  • 受检验硬币参与两次或以上的称量,且受检验硬币所在的天平一端与另一端的轻重光系不唯一。

AC代码:

#include<stdio.h>
#include<iostream>
#include<string.h>
#include<algorithm>
using namespace std;
char str1[3][7],str2[3][7],str3[3][7];
int main()
{
	int n;
	scanf("%d",&n);
	while(n--)
	{
		for(int i = 0 ; i < 3; i++)
		scanf("%s %s %s",str1[i],str2[i],str3[i]);
		char c = 'A';//此处枚举12个字母代表的硬币是否为假硬币
		int weight = 0;//用于表示假币与真币的质量关系,1为重于真币,-1则为轻	
		for(int i = 0 ; i < 3; i++)
		{
			int flag = 0; //用于检验该硬币是否在本轮参与称量
			for(int j = 0 ; str1[i][j] ; j++)
			{
				if(str1[i][j] == c || str2[i][j] == c)
	    			{
	     				flag = 1;
	     				if(str3[i][0] == 'e')
	     				{//两边相等则该硬币为真币,检验下一枚硬币 
	      					c++;
	      					i = -1;
	      					weight = 0;
	      					break;
				    	}
				     	else if(str3[i][0] == 'u')
				     	{//判断多次称量结果是否有差异,若有差异则该硬币为真币 
				      		if(str1[i][j] == c && weight == -1 || str2[i][j] == c &&weight == 1)
				      		{
						       	c++;
						       	i = -1;
						       	weight = 0;
						       	break;
						      	}
						      
				      		if(str1[i][j] == c)
					      		weight = 1;
					      	else
					       		weight = -1;
					     	}
		     			else
	    				{
	      					if(str1[i][j] == c && weight == 1 || str2[i][j] == c &&weight == -1)
						{
							c++;
						      	i = -1;
						      	weight = 0;
						       	break;
						      }
						      
					      	if(str1[i][j] == c)
				       			weight = -1;
				      		else
				       			weight = 1;
				     	}
	    			}
	   		}
	  		if(flag == 0)
	   		{//若该硬币本轮未参与测量,而测量结果依旧是不平衡,说明该硬币不是假硬币 
	    			if(str3[i][0] != 'e')
	    			{
				     	i = -1;
				     	weight = 0;
				     	c++;
				} 
	   		}
	  	}
	  	printf("%c is the counterfeit coin and it is ",c);
	  	if(weight == 1)
	   		printf("heavy.\n");
	  	else 
	  	 	printf("light.\n"); 
	 }
	 return 0;
}

暴力

同样由题目保证必定能检验出假币,则我们只要每一轮可确定真币的硬币及从未参与不平衡称量的硬币(因为题目保证能检验出假币及假币与真币的轻重对比,则若假币则必定参与不平衡称量)标记为真币,同时,对于参与多次称量的硬币,若该硬币所在天平一端的状态有高有低,则该硬币必不为假币。
对于每次称量都可做出如下标记:

  • 若天平平衡,则本次参与称量的硬币均为真币
  • 若天平不平衡,则本次未参与称量的硬币均为真币

同时,在每一次天平不平衡的称量中,均对称量的所有硬币标记其所在天平一端的状态。

则在称量结束后,对于未被标记为真币中,找出有被记录参与过不平衡称量,且参与的不平衡称量中,所在的天平一端状态唯一的硬币,该硬币即为假币,记录下的不平衡称量该硬币所在一端的状态即为假币与真币的状态对比(具体实现看代码备注)。

AC代码:

#include<stdio.h>
#include<iostream>
#include<string.h>
#include<algorithm>
using namespace std;
int main()
{
	int n;
	int real[15];//记录第 i 枚硬币的真假,若为 1 则为真,为 0 则表示暂无法确定真假 
	int weight[15];
	//记录多次称量中,第 i 枚硬币所在的天平一端的轻重状态,
	//若参与某次称量后,第 i 枚硬币所在的一端较重,则记录加一,较轻则减一 ,平衡不变 
	//由于有假币参与的称量中,天平必定不平衡,故在找到假币后
	//检查假币在多次称量中所在一端的状态记录,即可知道假币与真币的轻重对比 
	int uncertainty[15];
	//当天平两端不平衡时,不参与称量的硬币必为真币,记录参与称量的硬币,便于将不参与称量的硬币更新为真
	char str1[7],str2[7],str3[7];
	scanf("%d",&n);
	while(n--)
	{
	  	memset(real,0,sizeof(real));
	  	memset(weight,0,sizeof(weight));
	  	for(int k = 0 ; k < 3 ; k++)
	  	{
	  		scanf("%s %s %s",str1,str2,str3);
	   		if(str3[0] == 'e')
	   		{
	    		//天平平衡,将参与称量的硬币更新为真币 
	    			for(int i = 0 ; i < strlen(str1);i++)
	    			{
	     				real[str1[i] - 'A'] = 1;
	     				real[str2[i] - 'A'] = 1;
	    			}
	   		}
	   		else
	   		{
	   		//由于只有一枚假币,则若天平不平衡,则无法确定被称量的硬币真假,但不在本次称量的硬币则必为真 
	    			memset(uncertainty,0,sizeof(uncertainty)); 
	    			for(int i = 0 ; i < strlen(str1);i++)
	    			{
	     			//将参与称量的硬币放入不确定硬币数组 
	     				uncertainty[str1[i] - 'A'] = 1;
	     				uncertainty[str2[i] - 'A'] = 1;
	     				//更新本次称量各硬币所在一端的轻重对比情况 
	     				if(str3[0] == 'u')
	     				{
					      	weight[str1[i] - 'A'] ++;
					      	weight[str2[i] - 'A'] --;
	     				}else
	     				{
				      		weight[str1[i] - 'A'] --;
				      		weight[str2[i] - 'A'] ++;
	     				}
	    			}
	    			//将不在不确定硬币数组里的硬币确定为真币 
		    		for(int i = 0; i < 12;i++)
		    		{
		     			if(!uncertainty[i])
		      				real[i] = 1;
		    		}
		   	}
		}
		for(int i = 0 ; i < 12 ; i++)
		{//遍历所有硬币,寻找尚未确认为真且轻重对比不为 0 的那一枚硬币 
		 //为0则说明没参与称量与题意所说必定能称出假币及其轻重不符 
		 //或 参与两次称量且其所在天平一端的轻重状态不唯一,该币必不是假币
		 	if(real[i] == 0 && weight[i] != 0)
			{
			   	printf("%c is the counterfeit coin and it is ",i + 'A');
			    	if(weight[i] < 0)
			     		printf("light.\n");
			    	else
			     		printf("heavy.\n");
			    	break;
		   	}
		}
	}
	return 0;
}
  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值