百练-2692:假币问题(枚举)

总时间限制: 1000ms
内存限制: 65536kB

描述

赛利有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. 

思路

对于每一枚硬币先假设它是轻的,看这样是否符合称量结果。如果符合,问题即解决。如果不符合,就假设它是重的,看是否符合称量结果。把所有硬币都试一遍,一定可以找到特殊硬币。

思路看起来很简单,但是实现起来,每个人都有不同的方法。我的方法就跟我老师的不大一样。虽然我的也过了,但是我觉得老师的代码更好,所以说,好好听课还是很有必要的,学无止境(ง •_•)ง。

好代码:

#include 
#include 
char left[3][7]; //天平左边硬币 
char right[3][7]; //天平右边硬币 
char result[3][7]; //称量结果(up down even)

//返回值:假设是否成立
//light为真表示假设假币为轻
//否则表示假设假币为重 
bool isfake(char c, bool light);

int main(void)
{
      int t; //测试数据的组数 
      char c; 
      scanf("%d", &t);
      getchar(); //读入缓冲区的回车 
      while(t--)
      {
           //读入3次称重结果 
           //ABCD EFGH even
           //ABCI EFJK up
           //ABIJ EFGH even 
           for(int i=0; i<3; i++)
           {
                scanf("%s", left[i]);
                scanf("%s", right[i]);
                scanf("%s", result[i]);
           }
          //枚举假设为假的那一枚硬币 
           for(c='A'; c<='L'; c++)
           {
                  //假设c为轻的假币 
                 if(isfake(c, true))
                 {
                      printf("%c is the counterfeit coin and it is light.\n", c);
                      break;
                 }
                 //假设c为重的假币 
                 else if(isfake(c, false))
                 {
                       printf("%c is the counterfeit coin and it is heavy.\n", c);
                       break;
                 }
           }
      }
      return 0;
}

//返回值:假设是否成立
//light为真表示假设假币为轻
//否则表示假设假币为重
bool isfake(char c, bool light)
{
       //标志位,称量结果是否满足假设 
       bool flag=true;
 
       //假设带入到3次的称量结果,看和假设是否矛盾 
       for(int i=0; i<3; ++i)
       {
             //指向天平两边的字符串 
             char *pLeft, *pRight;
             if(light) //假设c为轻的假币 
             {
                   pLeft = left[i]; //指向第i次称量,天平左边的硬币集合 
                   pRight = right[i]; //指向第i次称量,天平右边的硬币集合
             }
             else
             {
                   //如果假设假币时重的,把称量结果左右对换 
                   pLeft = right[i];
                   pRight = left[i];
             }
  
             switch(result[i][0])//天平右边的情况 
             {
                   case 'u':
                         if(strchr(pRight,c)==NULL)
                         flag = false;
                         break;
                   case 'e':
                         if(strchr(pLeft,c) || strchr(pRight,c))
                         flag = false;
                         break;
                   case 'd':
                         i/f(strchr(pLeft,c)==NULL)
                         flag =  false;
                         break;
             }  
      }
      return flag;
}

我的代码:

#include 
#include 
int n,vis[200],v[3],l[3];
char a[3][15],b[3][15],c[3][15];
bool check(int x,bool y);//检查如果是轻或重,是否对
//y=0,假设轻,y=1,假设重 
int main(void)
{
	scanf("%d",&n);
	for(int i='A';i<='L';i++) vis[i]=1;    
    //这里我赋初值都为1,那接下来我枚举的时候,如果假设他是轻的,那我就把他赋值0,   
    //如果是重的就赋值为2,这样通过比较两边的重量和就可以得到天平的状态,
    //拿这个状态,与题目给的状态相比较,通过这个来判断是不是符合条件
	while(n--)
	{
		for(int i=0;i<3;i++)
		{
			scanf("%s %s %s",a[i],b[i],c[i]);
		}
		for(int i=0;i<3;i++)//先记录每行的状态
		{
			if(strcmp(c[i],"even")==0) v[i]=0;
			if(strcmp(c[i],"up")==0) v[i]=1;
			if(strcmp(c[i],"down")==0) v[i]=-1;
			l[i]=strlen(a[i]);//求两边字母的长度
		}
		for(int i='A';i<='L';i++)//枚举
		{
			if(check(i,0))
			{
				printf("%c is the counterfeit coin and it is light. \n",i);
				break;
			}
			if(check(i,1))
			{
				printf("%c is the counterfeit coin and it is heavy. \n",i);
				break;
			}
		}
	}
	return 0;	
} 
bool check(int x,bool y)
{
	int flag=0;
	if(y==0) vis[x]=0;//假设此字母是轻的
	else vis[x]=2;//假设此字母是重的
	for(int j=0;j<3;j++)
	{
		int sum1=0,sum2=0;
                for(int k=0;k<l[j];k++)  sum1+=vis[a[j][k]];//左边质量 
		for(int k=0;k<l[j];k++)  sum2+=vis[b[j][k]];//右边质量 
		if(v[j]==0) //相等 
		{
			if(sum1!=sum2) flag=1; 
		}
		else if(v[j]==1) //up 
		{
			if(sum1<=sum2) flag=1; 
		}
		else if(v[j]==-1) //down 
		{
			if(sum1>=sum2) flag=1; 
		}
		if(flag==1) 
		{
			vis[x]=1; //返回前先把字母重新赋值回1 
			return 0;
		}
	}
	vis[x]=1;return 1;
}

你可能感兴趣的:(枚举)