C語言程序設計100例之(78):撲克游戲


例78  撲克游戲

問題描述

亞當和夏娃用一副52張的普通牌玩紙牌游戲。規則很簡單,兩人面對面坐在桌子的兩側。每人從牌堆中取出k張牌,看了之后,將牌面朝下放在桌子上。亞當的牌從左邊的1到k編號,夏娃的牌從右邊的1到k編號(所以夏娃的第i張牌與亞當的第i張牌相對)。卡片正面朝上,積分如下:

如果亞當的第i(i∈ {1,…,k})張牌勝過夏娃的第i張牌,那么亞當得1分。

如果夏娃的第i張牌勝過亞當的第i張牌,那么夏娃得一分。

點數較高的牌總是勝過點數較低的牌:3勝2、4勝3、…。1張A牌勝過每1張牌,除了(可能)另1張A牌。

如果兩張牌的點數相同,那么牌的花色就決定了誰贏:紅桃勝過所有其他牌,黑桃勝過除紅桃以外的所有花色,方塊只勝過梅花,梅花不勝過任何一種花色。

例如,紅桃10勝過黑桃10,黑桃10勝過方塊10,方塊10勝過梅花10。

這應該是一場機會游戲,但最近夏娃大部分時間都贏了,原因是她已經開始使用有標記的牌。換言之,她在亞當把牌翻過來之前就知道他桌上有哪些牌。利用這些信息,她可以先排好自己的牌,以便獲得盡可能多的積分。

你的任務是,根據亞當和夏娃的牌,確定夏娃如果打得最好會得到多少分。

輸入

輸入包括多組測試用例。第一行輸入將包含一個正整數N,給出測試用例的數量。

每個測試用例從一行開始,該行帶有一個正整數k<=26,這是每個玩家獲得的牌數。

下一行描述了亞當放在桌子上的k張牌,從左到右。

再下一行描述了夏娃的k張牌(但她還沒有把它們放在桌子上)。卡片由兩個字符描述,第一個是它的點數(2、3、4、5、6、7、8、9、T、J、Q、K或A),第二個是它的花色(C、D、S或H,分別表示梅花、方塊、黑桃或紅桃)。牌之間用空格隔開。因此,如果亞當的牌是梅花10,紅桃2和方塊J,那可以描述為 TC 2H JD。

輸出

對於每個測試用例,如果夏娃選擇了最佳的方式將卡片排列在桌子上,輸出其最多得分。

輸入樣例

3

1

JD

JH

2

5D TC

4C 5H

3

2H 3H 4H

2D 3D 4D

輸出樣例

1

1

2

        (1)編程思路。

        由於需要對撲克牌進行排序,而采用牌面字符串不方便進行排序,因此可以編寫函數int getVal(char card[5])將52張撲克牌中的某張由card字符串指定的牌面轉換為對應的整數值(8~59之一)。轉換時,牌面2C=8、2D=9、2S=10、2H=11、…、AC=56、AD=57、AS=58、AH=59。

        撲克牌的一張牌面包含點數和花色兩個信息,可以將點數2~9分別取2~9,T、J、Q、K分別取10、11、12、13,A取14,四種花色C、D、S、H分別取0、1、2、3,這樣每張牌按牌面可映射為一個整數值,映射公式為: 牌面值=點數*4+花色。

        這樣,對撲克牌的排序就可以轉換為對整數的排序。

        輸入亞當和夏娃各自所取的K張牌后,將每張牌按牌面轉換為一個整數值並分別保存到數組A和數組E中,然后將數組A和E分別按從小到大的順序排列好。

        為了求出夏娃的最多得分,可以采用貪心法求解。

        貪心策略是:如果夏娃當前最大的牌可以贏亞當最大的牌,那么讓這兩張牌比大小,贏得1分;如果夏娃當前最小的牌能贏亞當最小的牌,那么讓這兩牌比大小,贏得1分;如果上面兩個條件都不滿足,就讓夏娃當前最小的牌和亞當當前最大的牌比大小,讓亞當得1分,但用掉其最大的牌。

        (2)源程序。
#include <stdio.h>
void sort(int a[],int n)
{
    int i,j,tmp;
    for (i=0;i<n-1;i++)
        for (j=0;j<n-1-i;j++)
           if (a[j]>a[j+1])
           {
              tmp=a[j]; a[j]=a[j+1]; a[j+1]=tmp;
           }
}
int value(char card[])
{
    int p,s;
    if (card[0]>='2' && card[0]<='9')
        p=card[0]-'0';
    else if (card[0]=='T') p=10;
    else if (card[0]=='J') p=11;
    else if (card[0]=='Q') p=12;
    else if (card[0]=='K') p=13;
    else if (card[0]=='A') p=14;
    if (card[1]=='C') s=0;
    else if (card[1]=='D') s=1;
    else if (card[1]=='S') s=2;
    else if (card[1]=='H') s=3;
    return 4*p+s;
}
int main()
{
   int n;
   scanf("%d",&n);
   while (n--)
   {
       int a[27],e[27];
       int k;
       scanf("%d",&k);
       char card[3];
       int i;
       for (i=0;i<k;i++)
       {
           scanf("%s",card);
           a[i]=value(card);
       }
       for (i=0;i<k;i++)
       {
           scanf("%s",card);
           e[i]=value(card);
       }
       sort(a,k);
       sort(e,k);
       int cnt=0;
       int aBegin=0,aEnd=k-1;
       int eBegin=0,eEnd=k-1;
       while (eBegin<=eEnd)
       {
           if (e[eBegin]>a[aBegin])
           {
               aBegin++;  eBegin++;  cnt++;
           }
           else if (e[eEnd]>a[aEnd])
           {
               aEnd--;  eEnd--;  cnt++;
           }
           else
           {
               eBegin++;   aEnd--;
           }
       }
       printf("%d\n",cnt);
   }
   return 0;
}

習題78

78-1  紙牌游戲

問題描述

Alice和她的朋友Bob正在玩一種紙牌游戲。這個游戲里要用到一副 2N張牌的套牌,編號從1到2N。Alice和Bob每個人各分得N張卡牌。接下來進行N輪比賽,Alice和Bob每輪各出一張牌。每一輪誰的牌編號更大,誰就贏得了本輪的勝利。

Alice已經預測了Bob的出牌順序,請幫助Alice算出她最多能贏多少輪。

輸入

第一行一個整數 N(1≤N≤5×104 )。

接下來N行,第i行一個整數,表示Bob第i輪出的牌。注意Alice手中的N張牌很容易從輸入中推出。

輸出

輸出Alice最多能贏多少輪。

輸入樣例

3

1

6

4

輸出樣例

2

說明/提示

Alice手中拿着 2,3,5三張牌。

她第一輪出2,第二輪出3,第三輪出5,從而贏得一,三兩輪。可以證明不存在更優的方案。

         (1)編程思路。

        定義數組int h[100005];初始值全部為0。依次輸入Bob的N張卡牌的編號,對於每個編號x,置h[x]=1。

        定義數組int a[50001],b[50001]分別保存Alice和Bob手里的卡牌,輸入結束后,對數組h進行掃描,將h[i]=0的下標i依次保存到數組a中,這是Alice所取的卡牌;將h[i]=1的下標i依次保存到數組b中,這是Bob所取的卡牌。顯然,數組a和數組b均已按卡牌的編號從小到大排列好了。

        為了讓Alice贏最多的輪次,可以采用貪心法求解。

        貪心策略是:如果Alice當前編號最大的牌可以贏Bob當前編號最大的牌,那么讓這兩張牌比大小,Alice贏1輪;如果Alice當前編號最小的牌能贏Bob當前編號最小的牌,那么讓這兩牌比大小,Alice贏1輪;如果上面兩個條件都不滿足,就讓Alice當前編號最小的牌和Bob當前編號最大的牌比大小,讓Bob贏這1輪,但這輪用掉了Bob編號最大的牌。

        (2)源程序。

#include <stdio.h>
int main()
{
    int h[100005]={0},a[50001],b[50001];
    int n;
    scanf("%d",&n);
    int i;
    for (i=1;i<=n;i++)
    {
       int x;
       scanf("%d",&x);
       h[x]=1;
    }
    int ak=1,bk=1;
    for (i=1;i<=2*n;i++)
    {
        if (h[i]==0) a[ak++]=i;
        else  b[bk++]=i;
    }
    int cnt=0;
    int aBegin=1,aEnd=n;
    int bBegin=1,bEnd=n;
    while (aBegin<=aEnd)
    {
        if (a[aBegin]>b[bBegin])
        {
            aBegin++;  bBegin++;  cnt++;
        }
        else if (a[aEnd]>b[bEnd])
        {
            aEnd--;  bEnd--;  cnt++;
        }
        else
        {
            aBegin++;   bEnd--;
        }
    }
    printf("%d\n",cnt);
    return 0;
}
78-2  嚴格遞增的序列

問題描述

給定一個長度為n的序列,你可以將序列中某些元素各減去一個數,使得整個序列嚴格遞增。

你需要求出所有減去的數的總和的最小值。

例如:有一個長度為3的序列 5,5,5;最優方案是 5-2,5-1,5即 3,4,5。這樣所有減去的數的總和是 2+1=3,為最小值。

輸入

輸入第一行一個整數 n(1≤n≤100),表示序列的長度。

第二行n個整數,描述這個序列。

輸出

輸出一行一個整數,表示總和的最小值。

輸入樣例

4

5

3

7

5

輸出樣例

6

        (1)編程思路。

         要是序列嚴格遞增,即序列的下一個數必須至少比前1個數大1,為了使所有減去的數的總和值最小,因此,貪心策略為:如果前1個數不小於后1個數,則把前1數減至比后1數小1即可。

        具體處理時,從第n-1個數開始倒推處理到第1個數,即最后的第n個數不變。

        若第i(i從n-1循環處理到1)個數比第i+1個數大(或相等),讓第i個數等於第i+1個數-1,並累計它們的差值。

        (2)源程序。

#include <stdio.h>
int main()
{
    int n;
    scanf("%d",&n);
    int a[105];
    int i;
    for (i=1;i<=n;i++)
        scanf("%d",&a[i]);
    int ans=0;
    for (i=n-1;i>=1;i--)
        if (a[i]>=a[i+1])
        {
            ans+=a[i]-a[i+1]+1;
            a[i]=a[i+1]-1;
        }
    printf("%d\n",ans);
    return 0;
}
78-3  卡牌游戲

問題描述

小明某天想到了一個卡牌游戲,游戲規則如下:

初始時小明的手中有自左向右排成一排的n 張卡牌,每張卡牌上有一個整數分值。

接下來,小明每次可以選取卡牌序列最左邊的連續若干張卡牌(至少2張),將它們替換為一張新卡牌。新卡牌將插入到序列的最左端,它的分值為本次操作中被替換掉的卡牌的分值之和。

初始時小明總分為0,每執行一次卡牌替換操作,新卡牌的分值將加到總分中。當序列長度為1時游戲結束,小明也可以在任意時刻結束游戲。

現在給出序列中各個卡牌的分值,請你來幫助小明計算他能夠獲得的最高總分是多少?

輸入

第一行一個正整數n,代表卡牌的數目。

接下來一行n個以空格分隔的整數,第 i個數字ai代表自左向右第i張卡牌的分值。

(1≤n≤105,ai≤105

輸出

僅一行一個整數表示答案。

輸入樣例

7

-4 3 0 7 -3 -5 -3

輸出樣例

9

樣例解釋

最優策略為,首先選擇最左側的四張卡牌,總分增加 (-4) + 3 + 0 + 7 = 6。此時小明選擇的四張卡牌被替換為一張分值為6 的卡牌,且被放入序列最左側,此時自左向右卡牌的分值為 6, -3, -5, -3。

再選擇最左側的兩張卡牌,總分增加 6 + (-3) = 3,總分為9。此時小明選擇的兩張卡牌被替換為一張分值為3的卡牌,且被放入序列最左側,此時自左向右卡牌的分值為 3, -5, -3。

此時無論如何操作均無法使總分繼續增大,小明選擇結束游戲。

         (1)編程思路。

        定義變量sum來保存輸入的卡牌分值的前綴和,初始化sum=第1張牌的分值,定義長整型變量ans保存能夠獲得的最高總分,初始值為0。

        用循環從第2張卡牌開始依次讀入第i張卡牌的分值x,並累加到sum上去,若sum>0,則將sum再累加到ans上,表示從第1張牌到第i張牌替換為一張分值為sum的新卡牌;若sum<=0,則不能增大總分值,因此不替換。

       (2)源程序。

#include <stdio.h>
int main()
{
    int n;
    scanf("%d",&n);
    long long x,sum;
    scanf("%lld",&x);
    sum=x;
    long long ans=0;
    int i;
    for (i=2;i<=n;i++)
    {
        scanf("%lld",&x);
        sum+=x;
        if (sum>0) ans+=sum;
    }
    printf("%lld\n",ans);
    return 0;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM