---
# System prepended metadata

title: 淘汰遊戲

---

# 淘汰遊戲
:::success

有11 個人圍成一個圓圈，如下圖表示。從箭頭所指為開始，每隔一人，就淘汰一人，從1號開始編號，編號1開始不淘汰，（例如3號離開，5號離開…，依此類推，若超過編號11號就接回到1號繼續淘汰下去），請問最後剩下來的那個人，是編號幾號？
:::
![](https://i.imgur.com/badBuRb.png)


## 輸入說明
每次輸入兩個數字
$n$與$p$,$n$表示每次參加淘汰遊戲的人數，$p$表示間隔多少人淘汰一人，輸入$n$小於100，且$p$<$n$。

## 輸出說明
輸出一個數字，表示最後剩下一人的編號。

## 輸入範例
11 3

## 輸出範例
10

## 解題構思
1. 使用陣列進行模擬，宣告一個101個元素的陣列，陣列第一個元素不使用，也就是陣列索引值為0的元素不使用。
2. 先設定陣列中所有元素為0，若被淘汰改成1。
3. 一個變數紀錄淘汰幾個了，當剩最後一個時，就使用迴圈找出陣列值為0 的索引值，就是答案。

### 參考程式
```cpp=
#include <iostream>
#include <cstring>   //for memset()
#define     MAX     100+5  //設定最大人數
using namespace std;
int a[MAX];   // Array to store users

int main()
{
    int n;  //總人數
    int p;  //間隔數
    // 不斷讀取輸入的 n, p 直到輸入結束後停止
    while(cin>>n>>p)
    {
        //將陣列的初始值全部設為0
        //0:未淘汰    1:被淘汰
        memset(a, 0, sizeof(a));
        //模擬淘汰過程
        int outs=0;  //紀錄累計淘汰的人數
        int steps;   //從目前位置往前累計的次數
        int curPos=2; //記錄下一個要被淘汰的位置
        while(outs<(n-1))
        {
            steps = 0;
            while(steps<p)
            {
                if(!a[curPos])
                {
                    //累計間隔人數 + 1
                    steps++;
                    //如果目前位置的索引還未到最大值(n)
                    //目前位置的索引 + 1
                    if(curPos<n)
                        curPos++;
                    //設成從第1個位置索引
                    else curPos=1;
                }

                else
                {
                    //如果目前位置的索引還未到最大值(n)
                    //目前位置的索引 + 1
                    if(curPos<n)
                        curPos++;
                    //設成從第1個位置索引
                    else curPos=1;
                }
            }
            //由 curPos往後找到下一個未被淘汰的位置
            while(a[curPos])
            {
                //如果目前位置的索引還未到最大值(n)
                //目前位置的索引 + 1
                if(curPos<n)
                    curPos++;
                //設成從第1個位置索引
                else curPos=1;
            }
            //curPos的位置被淘汰
            a[curPos] = 1;
            //累計淘汰人數
            outs++;

        }
        //輸出結果
            for(int i=1; i<=n; i++)
            {
                if(!a[i])
                {
                    cout<<i<<"\n";
                    break;
                }
            }
    }
    return 0;
}

```
![](https://i.imgur.com/O4OliVU.png)


# 服務顧客（模擬時間的進行）

## 題目說明
1. 便利商店店員服務顧客，請幫他計算沒有顧客的時間。
2. 已知顧客到達時間與服務所需時間，假設店員服務時不用休息，當顧客到時店員若在服務其他客人，則顧客會依序排隊等待服務，若超過該店員服務時間則由下一位店員服務。
3. 時間以分鐘為單位，同時間到的客人可以任意順序進行排隊，請計算店員在指定上班時間，沒有顧客的總時間，以分鐘為單位。

## 輸入說明
1. 每次輸入兩個數字$t$與$n$，$t$表示店員要上班幾小時，$n$表示有幾個客人需要服務。
2. 接著後面有$n$行，每行兩個數字$a$與$b$。
3. $a$表示顧客到達時間，所輸入的$a$為採用將小時與分鐘累加為累計分鐘數呈現，累計分鐘數從0開始，第一個小時為0到59，第二個小時為60到119,例如:80，表示第2小時的第20分鐘結束時，第2小時的第21分鐘剛開始時到便利商店。
4. $b$表示顧客服務所需時間，以分鐘為單位。
5. 所輸入資料會以顧客到達時間進行排序，$n$小於200人，$t$小於10小時。

## 輸出說明

輸出一個數字，表示店員沒有顧客的總時間，以分鐘為單位。

## 輸入範例

4 5
1 3
45 20
160 3
161 80
170 10

## 輸出範例

137

## 解題構思

1. 使用陣列進行模擬，宣告一個600個元素的陣列 $ser$ 先設定為0，若需要服務客人改成1。
2. 一個變數 $now$ 紀錄服務目前客人完成的時間，不斷經由客人到達時間與服務時間，更新陣列 $services$ 與變數$now$。
3. 最後統計服務時間內的陣列 $ser$ 的值等於0的個數。

### 參考程式
```cpp=
#include <iostream>

#include <cstring>       // for memset()
#define     MAXN    200  // MAX. of the customers
#define     MAXT    600  // MAX. of the working time
using namespace std;

int arroveTime[MAXN];        //紀錄MAXN個顧客到達時間
int serviceTime[MAXN];       //紀錄MAXN個顧客所需服務時間
int services[MAXT];          //記錄所有上班時間每分鐘的狀態


int main()
{
    int t;                  //儲存輸入的工作時間
    int n;                  //儲存有幾個客人需要服務
    while(cin>>t>>n){
        //n個客戶到達時間與所需服務時間存入陣列
        for(int i=0; i<n; i++){
            cin>>arroveTime[i]>>serviceTime[i];
        }
        //將記錄所有上班時間每分鐘的狀態的陣列全設為0
        memset(services, 0, sizeof(services));

        //模擬動作
        int totals = t*60;  //總工作時間分鐘
        int now = -1;       //每次開始服務時間(累加)
        for(int i=0; i<n; i++){
            //判斷是否在上班時間
            if(now>=totals)
                break;
            //不須排隊
            if(now<=arroveTime[i]){
                //將第i個人到達時間至服務時間的間隔設為1
                for(int j=arroveTime[i]; j<arroveTime[i]+serviceTime[i]; j++){
                    services[j]=1;
                }
                now = arroveTime[i]+serviceTime[i];
            }else{ //需要排隊
                //目前可以服務時間至服務時間的間隔設為1
                for(int j=now; j<=now+arroveTime[i]; j++){
                    services[j]=1;
                }
                now = now+serviceTime[i];
            }
        }
        //輸出結果
        int ans=0;
        for(int i=0; i<totals; i++){
            if(!services[i])
                ans++;
        }
        cout<<ans<<"\n";
    }
    return 0;
}

```

![](https://i.imgur.com/MRcEvcL.png)


# 神奇的蝸牛

## 題目說明

1. 蝸牛天天往上爬，想爬到樹頂。
2. 題目只給你樹的高度，蝸牛白天往上爬，晚上休息就會往下掉，且隨著天數增加往下掉的距離會不斷增加，且往下掉增加的距離都是第一天晚上往下掉的距離的10%。
3. 例如：第一天往下掉1公尺，第二天往下掉1.1公尺，第三天往下掉1.2公尺依此類推。
4. 問第幾天爬到樹頂，或者會掉到地面，所有輸入的單位都以公尺為單位，而且都是整數。

## 輸入說明

每次輸入三個數字$h$、$d$與$n$，$h$表示樹的高度，$d$表示蝸牛白天爬幾公尺，$n$表示蝸牛第一天晚上往下掉幾公尺。

## 輸出說明

輸出一個數字，表示第幾天爬到樹頂，若是第一天就爬到樹頂，就輸出「第1天爬到樹頂」，或者第二天掉到地面，就輸出「第2天掉到地面」

## 輸入範例

100 5 1

## 輸出範例

第$84$天掉到地面

### 參考程式
```cpp=
#include <iostream>

using namespace std;

int main(){
    int h,d;
    double n;

    while(cin>>h>>d>>n){
        int day=0;    // 天數
        double curHei=0;    // 目前高度
        double down=double(n);
        double n01=n*0.1;   // n的10%

        cout<<"down="<<down;

        while(1){
            day++;
            curHei+=d;

            if(curHei>=h||curHei<=0) break;
            curHei-=down;
            down+=n01;
        }
        if(curHei>=h)   cout<<"第"<<day<<"天爬到樹頂\n";
        if(curHei<=0)   cout<<"第"<<day<<"天掉到地面\n";
    }
}
```


# 撲克牌比大小（模擬撲克牌遊戲進行）

## 題目說明
1. 兩位玩家各拿5張牌，比較兩位玩家手中的五張撲克牌的大小來決定勝負。
2. 個別牌的數字大小順序分別為$A$、$K$、$Q$、$J$、$T$、$9$、$8$、$7$、$6$、$5$、$4$、$3$、$2$。
3. 花色大小順序分別為為黑梅©、紅磚(D)、紅心(H)與黑桃(S)。
4. 所輸入資料一定可以區分兩位玩家的勝負，可以不考慮平手情形，牌由小到大如下表，編號越大牌越大，大者獲勝。
5. 這個範例很複雜，需要耐心依據題意解題。

![](https://i.imgur.com/OXr5eNK.png)
![](https://i.imgur.com/jjDjU5d.png)

## 輸入說明

1. 輸入十張牌前面五張牌表示第一位玩家的牌，後面五張牌表示第二位玩家的牌
2. 輸入的牌由兩個字元的花色與數字組合起來，例如$S$$A$表示黑桃$A$、$D$$T$表示紅磚$10$，$C$$J$
表示黑莓$J$，$H$$K$表示紅心$K$，依此類推。

## 輸出說明

若第一位玩家較第二位玩家較大，就輸出「第一位玩家獲勝」，若第二位玩家較第一位玩家較大，就輸出「第二位玩家獲勝」。

## 輸入範例

CJ CK CT C2 C4 D2 S2 H2 DJ DT

## 輸出範例

第一位玩家獲勝

## 解題構思

1. 將撲克牌黑莓2到黑莓A轉換成數字0到12；
紅磚2到紅磚A轉換成數字0到12；
紅心2到紅心A轉換成數字0到12；
黑桃2到黑桃A轉換成數字0到12。
2. 利用將第一位與第二位玩家的撲克牌傳成數字後分別儲存到陣列fp（第一位）與陣列sp（第二位），由小到大排序fp 與sp，並統計兩位玩家五張牌中，不考慮花色只考慮數字，五張牌的數字出現個數到陣列f（第一位）與陣列s（第二位）。
3. 第一位玩家撲克牌的大小等級可以利用陣列fp 與f 進行判斷，第二位玩家撲克牌的大小等級可以利用陣列sp 與s 進行判斷。若兩位玩家的撲克牌大小等級相同，則需另外判斷最高位的牌區分出哪一方獲勝。

### 參考程式
```cpp=
#include <iostream>

#include <string>    //for string 物件
#include <sstream>   //for stringstream 物件
#include <cstring>   //for memset()
#include <algorithm> //for algorithm
#define     CARDS   13   //每種花色有13張牌
#define     SIZE    5    //每個人的牌數
using namespace std;

int user1[CARDS];    //紀錄2.3...A在5張牌中出現的次數
int user2[CARDS];    //紀錄2.3...A在5張牌中出現的次數
int src1[5];         //紀錄原來5張牌的編號
int src2[5];         //紀錄原來5張牌的編號

/*
將 poker 依據花色與點數編碼成 0~51的數字
1.梅花 1~12
2.方塊 13~25
3.紅心 26~38
4.黑桃 39~51
*/
int getCardNO(string s)
{
    int suit, num;    //紀錄花色, 點數
    switch(s[0]){  // 花色
    case 'C':       //1~12
        suit=0; break;
    case 'D':       //3~25
        suit=1; break;
    case 'H':       //26~38
        suit=2; break;
    case 'S':       //39~51
        suit=3; break;
    }
    switch(s[1]){  //點數
    case 'A':
        num=12; break;
    case 'K':
        num=11; break;
    case 'Q':
        num=10; break;
    case 'J':
        num=9; break;
    case 'T':
        num=8; break;
    default:
        num=s[1]-'2'; break;
    }
    // 回傳轉換後的編碼
    return (suit*CARDS+num);
}
void showArray(int *poker, int size)
{
    for(int i=0; i<size; i++){
        cout<<poker[i]<<" ";
    }
    cout<<"\n";
}
//判斷是否為Straight Flush同花順
bool isStraightFlush(int* src, int* points)
{
    //Assume: 1 2 3 4 5
    //最大點數 - 最小點數 = 4
    if(src[4]-src[0]==4){
        // 判斷5張牌的花色是否相同
        //5張牌是梅花
        if(src[0]>=CARDS*0 && src[4]<CARDS*1) return true; //  0~12 為 梅花
        //5張牌是方塊
        if(src[0]>=CARDS*1 && src[4]<CARDS*2) return true; // 13~25 為 方塊
        //5張牌是紅心
        if(src[0]>=CARDS*2 && src[4]<CARDS*3) return true; // 26~38 為 紅心
        //5張牌是黑桃
        if(src[0]>=CARDS*3 && src[4]<CARDS*4) return true; // 39~51 為 黑桃
    }
    return false;
}
// 判斷 src 是否為Flush(同花)
// src 已經由小到大排序
bool isFlush(int* src,int* points)
{
    // 所有牌必須同一種花色
    if(src[0]>=CARDS*0 && src[4]<CARDS*1) return true;  //  0~12 為 club
    if(src[0]>=CARDS*1 && src[4]<CARDS*2) return true;  // 13~25 為 diamond
    if(src[0]>=CARDS*2 && src[4]<CARDS*3) return true;  // 26~38 為 heart
    if(src[0]>=CARDS*3 && src[4]<CARDS*4) return true;  // 39~51 為 Spade
    return false;
}
//判斷5張牌是否為Straight順子
//points:5張牌點數出現的次數
bool isStraight(int* src,int* points)
{
    // 5 6 7 8 9: 各出現1次
    for(int i=0; i<CARDS; i++){
        if(points[i]==1 &&
           points[i+1]==1 &&
           points[i+2]==1 &&
           points[i+3]==1 &&
           points[i+4]==1)
            return true;
    }
    return false;
}
//判斷5張牌是否為isFourOfKind(鐵支)
//points:5張牌點數有一次出現4次
bool isFourOfKind(int* src,int* points)
{
    // 0~12有一個數字出現4次
    for(int i=0; i<CARDS; i++){
        if(points[i]==4) return true;
    }
    return false;
}

// 判斷5張牌是否為 Full house(葫蘆)
bool isFullHouse(int* src,int* points)
{
    for(int i=0; i<CARDS; i++){
        // 出現 1對
        if(points[i]==2){
            // 出現 3條: i+1開始到12有一個點數出現 3次
            for(int j=i+1; j<CARDS; j++){
                if(points[j]==3) return true;
            }
        }
        // 出現 3條
        if(points[i]==3){
            // 出現 1對: i+1開始到12有一個點數出現 2次
            for(int j=i+1; j<CARDS; j++){
                if(points[j]==2) return true;
            }
        }
    }
    return false;
}

// 判斷5張牌是否為Three of a Kind(三條)
//points:5張牌點數有一次出現3次
bool isThreeOfKind(int* src,int* points)
{
    // 0~12有一個數字出現3次
    for(int i=0; i<CARDS; i++){
        if(points[i]==3) return true;
    }
    return false;
}

// 判斷5張牌是否為Two Pairs(兩對)
//points:5張牌點數有兩個數字出現2次
bool isTwoPairs(int* src,int* points)
{
    // 0~12有一個數字出現2次
    for(int i=0; i<CARDS; i++){
        if(points[i]==2){
            // 出現 1對: i+1開始到12有一個點數出現 2次
            for(int j=i+1; j<CARDS; j++){
                if (points[j]==2) return true;
            }
        }
    }
    return false;
}

// 判斷5張牌是否為One Pair(一對 )
//points:5張牌點數有一個數字出現2次
bool isOnePair(int* src,int* points)
{
    // 0~12有一個數字出現2次
    for(int i=0; i<CARDS; i++){
        if(points[i]==2) return true;
    }
    return false;
}
//由 src 與 points 陣列來得知5張牌的等級
int getLeve1(int* src, int* points)
{
    if(isStraightFlush(src,points)) return 9;  // 同花順
    else if(isFourOfKind(src,points)) return 8;   // 鐵支
    else if(isFullHouse(src,points)) return 7;    // 葫蘆
    else if(isFlush(src,points)) return 6;     // 同花
    else if(isStraight(src,points)) return 5;  // 順子
    else if(isThreeOfKind(src,points)) return 4;  // 三條
    else if(isTwoPairs(src,points)) return 3;     // 兩對
    else if(isOnePair(src,points)) return 2;      // 一對
    else return 1;                         // 散牌
}
int checkLevel(int leve)
{
    return 1;
}

int main()
{
    string s;         //讀取一行資料儲存的字串
    while(getline(cin, s)){
        //s為一行資料,資料間以空白隔開
        //以s初始化stringstream物件(ss)
        stringstream ss(s);
        //將 user1, user2 紀錄2.3...A在5張牌中出現的次數的陣列全部設為0
        memset(user1, 0, sizeof(user1));
        memset(user2, 0, sizeof(user2));

        //把輸入資料的前面5筆存入src1, user1
        //          把後面5筆存入src2, user2
        string tmp;
        int cardNo;
        for(int i=0; i<10; i++){
            //從ss串流讀取一個字串
            ss>>tmp;
            //將tmp編碼成0~51的數字
            cardNo = getCardNO(tmp);
            //前面5筆存入src1, user1
            if(i<5){
                src1[i]=cardNo;
                user1[cardNo%CARDS]++;
            }else{
                //後面5筆存入src2,user2
                src2[i%SIZE]=cardNo;
                user2[cardNo%CARDS]++;
            }
        }
        //由小到大將2個人的牌點數由小到大排序
        sort(src1, src1+SIZE);
        sort(src2, src2+SIZE);
        //showArray(src1, SIZE);
        //showArray(src2, SIZE);

        //取得5張牌的等級
        int level1 = getLeve1(src1, user1);
        int level2 = getLeve1(src2, user2);
        if(level1>level2) cout<<"第一位玩家獲勝\n";
        else if(level1<level2) cout<<"第二位玩家獲勝\n";
        else{
            int check = checkLevel(level1);
            (check==1) ? (cout<<"第一位玩家獲勝\n") : (cout<<"第二位玩家獲勝\n");
        }
    }


    return 0;
}

```
