基础算法之二——枚举法

Posted Clitter的博客

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了基础算法之二——枚举法相关的知识,希望对你有一定的参考价值。

基础算法之二——枚举法“赛利的硬币”

题目描述

赛利有 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;
}

以上是关于基础算法之二——枚举法的主要内容,如果未能解决你的问题,请参考以下文章

《算法零基础》第18讲:线性枚举- 统计法入门

基础算法排序-复杂排序之二(找出第K大的数)

《算法零基础》第10讲:因子分解和枚举(部分)

算法基础 —— 枚举

算法入门 01线性枚举(简单 - 第二题)LeetCode 557

《统计学习方法》之二:感知机学习算法