07_紙牌游--小貓釣魚

<p>
星期天小哼和小哈約在一起玩桌游,他們正在玩一個非常古怪的撲克游戲——“小貓釣魚”。游戲的規則是這樣的:將一副撲克牌平均分成兩份,每人拿一份。小哼先拿出手中的第一張撲克牌放在桌上,然后小哈也拿出手中的第一張撲克牌,并放在小哼剛打出的撲克牌的上面,就像這樣兩人交替出牌。出牌時,如果某人打出的牌與桌上某張牌的牌面相同,即可將兩張相同的牌及其中間所夾的牌全部取走,并依次放到自己手中牌的 尾。當任意一人手中的牌全部出完時,游戲結束,對手獲勝。
</p>
<p>假如游戲開始時,小哼手中有 6 張牌,順序為 2 4 1 2 5 6,小哈手中也有 6 張牌,順序為 3 1 3 5 6 4,最終誰會獲勝呢?現在你可以拿出紙牌來試一試。接下來請你寫一個程序來自動判斷誰將獲勝。這里我們做一個約定,小哼和小哈手中牌的牌面只有 1~9。</p>

Paste_Image.png

<p>我們先來分析一下這個游戲有哪幾種操作。小哼有兩種操作,分別是出牌和贏牌。這恰好對應隊列的兩個操作,出牌就是出隊,贏牌就是入隊。小哈的操作和小哼是一樣的。而桌子就是一個棧,每打出一張牌放到桌上就相當于入棧。當有人贏牌的時候,依次將牌從桌上拿走,這就相當于出棧。那如何解決贏牌的問題呢?贏牌的規則是:如果某人打出的牌與桌上的某張牌相同,即可將兩張牌以及中間所夾的牌全部取走。那如何知道桌上已經有哪些牌了呢?最簡單的方法就是枚舉桌上的每一張牌,當然也有更好的辦法我們待會再說。OK,小結一下,我們需要兩個隊列、一個棧來模擬整個游戲。</p>
<p>首先我們先來創建一個結構體用來實現隊列,如下。</p>
<p>struct queue{ int data[1000]; int head; int tail; }</p>
<p>上面代碼中 head 用來存儲隊頭,tail 用來存儲隊尾。數組 data 用來存儲隊列中的元素,數組 data 的大小我預設為 1000,其實應該設置得更大一些,以防數組越界。當然對于 題的數據來說 1000 已經足夠了。</p>
<p> 再創建一個結構體用來實現棧,如下。</p>
<p>struct stack{ int data[10]; int top; };</p>
<p>其中 top 用來存儲棧頂,數組 data 用來存儲棧中的元素,大小設置為 10。因為只有 9種不同的牌面,所以桌上最多可能有 9 張牌,因此數組大小設置為 10 就夠了。提示一下:為什么不設置為 9 呢?因為 C 語言數組下標是從 0 開始的。</p>
<p>其中 top 用來存儲棧頂,數組 data 用來存儲棧中的元素,大小設置為 10。因為只有 9種不同的牌面,所以桌上最多可能有 9 張牌,因此數組大小設置為 10 就夠了。提示一下:為什么不設置為 9 呢?因為 C 語言數組下標是從 0 開始的。</p>
<p>struct queue q1,q2; struct stack s;</p>
<p> 接下來來初始化一下隊列和棧。</p>
<p>//初始化隊列q1和q2為空,此時兩人手中都還沒有牌 q1.head=1; q1.tail=1; q2.head=1; q2.tail=1; //初始化棧s為空,最開始的時候桌上也沒有牌 s.top=0;</p>
<p>接下來需要讀入小哼和小哈最初時手中的牌,分兩次讀入,每次讀入 6 個數,分別插入q1和q2中。</p>
<p>// 依次像隊列中插入6個數 // 小哼手中的牌 printf("請設置小哼手中的牌:\n"); for (i=1; i<=6; i++) { scanf("%d",&q1.data[q1.tail]); q1.tail++; } printf("\n"); printf("請設置小哈手中的牌:\n"); // 小哈手中的牌 for (i=1; i<=6; i++) { scanf("%d",&q2.data[q2.tail]); q2.tail++; } printf("\n");</p>
<p>現在準備工作已經基 上做好了,游戲正式開始,小哼先出牌。</p>
<p>t=q1.data[q1.head]; //小哼先亮出一張牌</p>
<p>小哼打出第一張牌,也就是 q1 的隊首,我們將這張牌存放在臨時變量 t 中。接下來我們要判斷小哼當前打出的牌是否能贏得桌上的牌。也就是判斷桌上的牌與 t 有沒有相同的,如何實現呢?我們需要枚舉桌上的每一張牌與 t 進行比對,具體如下:</p>
<p>flag=0; for(i=1;i<=top;i++) { if(t==s[i]) { flag=1; break; } }</p>
<p>如果 flag 的值為 0 就表明小哼沒能贏得桌上的牌,將打出的牌留在桌上</p>
<p>if(flag==0){ //小哼此輪沒有贏牌 q1.head++; //小哼已經打出一張牌,所以要把打出的牌出隊 s.top++; s.data[s.top]=t; //再把打出的牌放到桌上,即入棧 }</p>
<p>如果 flag 的值為 1 就表明小哼可以贏得桌上的牌,需要將贏得的牌依次放入小哼的手中。</p>
<p>if(flag==1) { //小哼此輪可以贏牌 q1.head++;//小哼已經打出一張牌,所以要把打出的牌出隊 q1.data[q1.tail]=t; //因為此輪可以贏牌,所以緊接著把剛才打出的牌又放到手中牌的對尾 q1.tail++; while(s.data[s.top]!=t) //把桌上可以贏得的牌(從當前桌面最頂部一張牌開始取,直至取到與打出的牌相同為止)依次放到手中牌的末尾 { q1.data[q1.tail]=s.data[s.top]; //依次放入隊尾 q1.tail++; s.top--; //棧中少了一張牌,所以棧頂要減1 } }</p>
<p>小哼出牌的所有階段就模擬完了,小哈出牌和小哼出牌是一樣的。接下來我們要判斷游戲如何結束。即只要兩人中有一個人的牌用完了游戲就結束了。因此需要在模擬兩人出牌代碼的外面加一個 while 循環來判斷,如下。</p>
<p>while(q1.head<q1.tail && q2.head<q2.tail ) //當隊列q1和q2都不為空的時候執行循環</p>
<p>最后一步,輸出誰最終贏得了游戲,以及游戲結束后獲勝者手中的牌和桌上的牌。如果
小哼獲勝了那么小哈的手中一定沒有牌了(隊列 q2 為空),即 q2.head==q2.tail,具體輸出如下。</p>
<p>if(q2.head==q2.tail){ printf("小哼win\n"); printf("小哼當前手中的牌是"); for(i=q1.head;i<=q1.tail-1;i++) printf(" %d",q1.data[i]); if(s.top>0) //如果桌上有牌則依次輸出桌上的牌 { printf("\n桌上的牌是"); for(i=1;i<=s.top;i++) printf(" %d",s.data[i]); }else printf("\n桌上已經沒有牌了");} }</p>
<p>反之,小哈獲勝,代碼的實現也是差不多的,就不再贅述了。到此,所有的代碼實現就都講完了。</p>
<p>在上面我們講解的所有實現中,每個人打出一張牌后,判斷能否贏牌這一點可以優化。之前我們是通過枚舉桌上的每一張牌來實現的,即用了一個 for 循環來依次判斷桌上的每一張牌是否與打出的牌相等。其實有更好的辦法來解決這個問題,就是用一個數組來記錄桌上有哪些牌。因為牌面只有 1~9,因此只需開一個大小為 10 的數組來記錄當前桌上已經有哪些牌面就可以了。</p>
<p>int book[10];</p>
<p>這里我再一次使用了 book 這個單詞,因為這個單詞有記錄、登記的意思,而且單詞拼寫簡潔。另外很多國外的算法書籍在處理需要標記問題的時候也都使用 book 這個單詞,因此我這里就沿用了。當然你也可以使用 mark 等你自己覺得好理解的單詞啦。下面需要將數組 book[1]~book[9]初始化為 0,因為剛開始桌面上一張牌也沒有。</p>
<p>for(i=1;i<=9;i++) book[i]=0;</p>
<p>接下來,如果桌面上增加了一張牌面為 2 的牌,那就需要將 book[2]設置為 1,表示牌面為 2 的牌桌上已經有了。當然如果這張牌面為 2 的牌被拿走后,需要及時將 book[2]重新設置為 0,表示桌面上已經沒有牌面為 2 的牌了。這樣一來,尋找桌上是否有與打出的牌牌面相同的牌,就不需要再循環枚舉桌面上的每一張牌了,而只需用一個 if 判斷即可。這一點是不是有點像第 1 章第 1 節的桶排序的方法呢?具體如下。</p>
<p>t=q1.data[q1.head]; //小哼先亮出一張牌 if(book[t]==0) // 表明桌上沒有牌面為t的牌 { //小哼此輪沒有贏牌 q1.head++; //小哼已經打出一張牌,所以要把打出的牌出隊 s.top++; s.data[s.top]=t; //再把打出的牌放到桌上,即入棧 book[t]=1; //標記桌上現在已經有牌面為t的牌 }</p>
<p>OK,算法的實現講完了,下面給出完整的代碼,如下:</p>
<p>``

include <stdio.h>

include <string.h>

// 使用結構體 (queue) 來表示小哼和小哈手中的牌
struct queue{

int data[1000]; // 用戶手中的牌
int head;       // 隊首
int tail;       // 對尾

};

// 使用結構體 (stack) 來表示桌面上的牌
struct stack{

int data[10];  // 桌面上所有的牌
int top;       // 桌面上最上邊的牌

};

int main(){

struct queue q1, q2;  // 聲名小哼, 小哈
struct stack s;       // 聲名桌面上的牌
int book[10];  // 用來標記桌面上的牌
int i, t;

q1.head = 1;   // 初始化小哼
q1.tail = 1;

q2.head = 1;   // 初始化小哈
q2.tail = 1;

s.top = 0;     // 初始化棧, 桌面上最上邊的的牌的索引

// 初始化用來標記的數組, 用來標記哪些牌出現過
for (i=1; i<=9; i++) {
    book[i] = 0;
}

// 依次像隊列中插入6個數
// 小哼手中的牌
printf("請設置小哼手中的牌:\n");
for (i=1; i<=6; i++) {
    scanf("%d",&q1.data[q1.tail]);
    q1.tail++;
}
printf("\n");
printf("請設置小哈手中的牌:\n");
// 小哈手中的牌
for (i=1; i<=6; i++) {
    scanf("%d",&q2.data[q2.tail]);
    q2.tail++;
}
printf("\n");

// 當隊列不為空的時候執行循環 (判斷小哼小哈手中是否有牌)
while (q1.head < q1.tail && q2.head < q2.tail) {
    // 游戲開始
    
    // 小哼先出第一張牌
    t = q1.data[q1.head];
    // 判斷小哼是否能贏牌
    if (book[t] == 0) {
        // 沒有贏牌
        q1.head++; // 出隊
        s.top++;
        s.data[s.top] = t; // 把牌加入到棧頂
        book[t] = 1; // 標記桌面上已經出現了為 t 的牌
    }else{
        // 贏牌
        q1.head++;
        q1.data[q1.tail] = t;
        q1.tail++;
        
        while (s.data[s.top] != t) {
            
            // 取消標記
            book[s.data[s.top]] = 0;
            
            q1.data[q1.tail] = s.data[s.top];
            q1.tail++;
            s.top--;
        }
    }
    
    // 小哈出牌
    t = q2.data[q2.head];
    // 判斷小哈是否能贏牌
    if (book[t] == 0) {
        // 沒有贏牌
        q2.head++; // 出隊
        s.top++;
        s.data[s.top] = t; // 把牌加入到棧頂
        book[t] = 1; // 標記桌面上已經出現了為 t 的牌
    }else{
        // 贏牌
        q2.head++;
        q2.data[q2.tail] = t;
        q2.tail++;
        
        while (s.data[s.top] != t) {
            
            // 取消標記
            book[s.data[s.top]] = 0;
            
            q2.data[q2.tail] = s.data[s.top];
            q2.tail++;
            s.top--;
        }
    }
}

// 判斷輸贏
if (q2.head == q2.tail) {
    printf("小哼 win \n");
    printf("小哼當前手中的牌是:");
    for (i=q1.head; i<q1.tail; i++) {
        printf(" %d",q1.data[i]);
    }
    if (s.top > 0) {  // 如果桌上有牌則依次輸出桌上的牌
        printf("桌上的牌是:");
        for (i=1; i<=s.top; i++) {
            printf("%d",s.data[i]);
        }
    }else{
        printf("\n 桌上已經沒有牌了 ^o^ ");
    }
}else{
    printf("小哈 win \n");
    printf("小哈當前手中的牌是:");
    for (i=q1.head; i<q1.tail; i++) {
        printf(" %d",q1.data[i]);
    }
    if (s.top > 0) {  // 如果桌上有牌則依次輸出桌上的牌
        printf("桌上的牌是:");
        for (i=1; i<=s.top; i++) {
            printf("%d",s.data[i]);
        }
    }else{
        printf("\n 桌上已經沒有牌了 ^o^ ");
    }
}
printf("\n");


return 0;

} ``</p>

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,106評論 6 542
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,441評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,211評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,736評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,475評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,834評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,829評論 3 446
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,009評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,559評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,306評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,516評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,038評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,728評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,132評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,443評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,249評論 3 399
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,484評論 2 379

推薦閱讀更多精彩內容