1. 程式人生 > >程式設計菜鳥到大佬之路:演算法基礎(三)

程式設計菜鳥到大佬之路:演算法基礎(三)

第三天學習精要

列舉


稱硬幣

  • 例題3:POJ1013 稱硬幣
    • 題目描述
      • 有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。
    • 解題思路
      • 對於每一枚硬幣先假設它是輕的,看這樣是否符合稱量結果。
      • 如果符合,問題即解決。
      • 如果不符合,就假設它是重的,看是否符合稱量結果。
      • 把所有硬幣都試一遍,一定能找到特殊硬幣。
      # include <iostream>
      # include <cstring>
      using namespace std;
      char Left[3][7];     // 天平左邊硬幣,最多稱三次,左邊最多放六枚硬幣,例如Left[0][4]代表第一次稱量時左邊有四枚硬幣
      char Right[3][7];    // 天平右邊硬幣,最多稱三次,右邊最多放六枚硬幣
      char Result[3][7];   // 結果,可能是even,up,down
      bool IsFake(char c, bool light);    // light為真表示假設假幣為輕,否則表示假設假幣為重
      
      int main(){
          int t;  // t代表測試資料的組數
          cin >> t;
          while (t--){    // 對每一組資料把三次測量結果讀進來
              for (int i = 0; i < 3; ++i){
                  cin >> Left[i] >> Right[i] >> Result[i];    // 讀入的是三個字串,分別放入三個陣列
              }
              for (char c = 'A'; c <= 'L'; c++){
                  if (IsFake(c, true)){   // 列舉一個個找出假幣c,認為c是輕的假幣
                      cout << c << " is the counterfeit coin and it is light.\n";
                      break;
                  }else if (IsFake(c, false)){    // 列舉一個個找出假幣c,認為c是重的假幣
                      cout << c << " is the counterfeit coin and it is heavy.\n";
                      break;
                  }
              }
          }
          return 0;
      }
      
      bool IsFake(char c, bool light){
          // c表示假設哪一枚是假幣,light為真表示假設假幣為輕,否則表示假設假幣為重
          for (int i = 0; i < 3; ++i){    // 分析三次的稱量結果,三次都不矛盾,假設為真
              char *pLeft, *pRight;     // 指向天平兩邊的字串
              if (light){
                  pLeft = Left[i];    // Left[i]是指第i次稱量時天平左邊所放的字串
                  pRight = Right[i];  // Right[i]是指第i次稱量時天平右邊所放的字串
              }else{   // 如果假設假幣是重的,則把稱量結果左右對換
                  pLeft = Right[i];
                  pRight = Left[i];
              }
              switch (Result[i][0]){  // 天平右邊的情況,此時認為c是輕的假幣,看看第i次的稱量結果是否和假設矛盾
                  case 'u':   // 天平右邊高於左邊
                      if (strchr(pRight, c) == NULL)  // C應該出現在pRight所指向的字串,但是假幣c沒有出現在右邊
                          return false;       // 假設c是輕的假幣是錯誤的
                      break;
                  case 'e':   // 天平兩邊一樣高
                      if (strchr(pLeft, c) || strchr(pRight, c))  // c不應該出現在左邊或者右邊的字串,如果c出現了
                          return false;       // 假設c是輕的假幣是錯誤的
                      break;
                  case 'd':   // 天平右邊低於左邊
                      if (strchr(pLeft, c) == NULL)   // C應該出現在pLeft所指向的字串,但是假幣c沒有出現在左邊
                          return false;       // 假設c是輕的假幣是錯誤的
                      break;
              }
          }
          return true;
      }