现在的位置: 首页 > 综合 > 正文

2014 Multi-University Training Contest 6 Fighting the Landlords

2018年04月25日 ⁄ 综合 ⁄ 共 2418字 ⁄ 字号 评论关闭

题意:就是普通的斗地主,问你第一个人能不能打败第二个人

注意点:

1.三张牌出牌带的必须的一对,而四张牌带的可以是两张不同的

2.第一个人打完牌他就赢了,不用考虑后面的人

思路:

一共有8种出牌方法,前面6种只能按照牌面大小打败相同的出牌类型

但是最后两种,也是就玩炸和炸弹可以打败其他的出牌类型;

因此,我们考虑计算出每一个出牌类型,能出的最大的权重,

如果是最后两种就把他们的权重更新到其他6种出牌类型里面

最后统计的时候要注意的是:

1.第一个人的某一种出牌类型为0的时候不能比较,因为如果第二个人这一种出牌类型的最大权重也为0也就是说,他不能出这种牌,用大于等于判断的时候就会出错

2.第二个人出光手牌的情况不能被考虑,因为第二个人只能出与第一个人对应的出牌类型.

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <string>
#include <vector>
using namespace std;
const int Bomb=1e6; //炸弹的权重
const int Nuke=1e7;//玩炸的权重
const int EMPTY_HAND=1e9;//一次能光打牌的权重
bool is_first;//是否是第一个人
int card[16],apro[7],bpro[7];//各种权重卡片的个数,a的各种出牌类型的最大权重,b的各种出牌类型的最大权重
int *pro;//当前计算时权重的存储位置
int prio_ch(char a) //卡牌权重转换函数,从小到大
{
    switch(a)
    {
    case 'Y':
        return 15;
    case 'X':
        return 14;
    case '2':
        return 13;
    case 'A':
        return 12;
    case 'K':
        return 11;
    case 'Q':
        return 10;
    case 'J':
        return 9;
    case 'T':
        return 8;
    case '9':
    case '8':
    case '7':
    case '6':
    case '5':
    case '4':
    case '3':
        return a-'0'-2;
    }
}
void setv(int i,int v) //用v更新i类型出牌的最大权重
{
    pro[i]=max(pro[i],v);
}
void setall(int v) //用v更新全部类型出牌的最大权重
{
    if(!is_first && v==EMPTY_HAND) return ; //如果不是第一个人打光手牌的话则返回
    for(int i=1;i<=6;i++) pro[i]=max(pro[i],v);
}
void cal(string a)
{
    memset(card,0,sizeof(card));//初始化
    int n=a.size(),npair=0;
    for(int i=0;i<n;i++)
        card[prio_ch(a[i])]++; //计数
    if(card[15] && card[14]) //大小王都有就更新全部出牌类型为玩炸
        setall(Nuke);
    if(n==1)  //只有一张牌,所以可以打光
        setall(EMPTY_HAND);
    for(int i=1;i<=15;i++) //计算只出一张牌,也就是第一种出牌类型的权重
    {
        if(card[i]) setv(1,i);
    }

    for(int i=1;i<=15;i++) //第二种出牌类型的权重
    {
        if(card[i]>=2)
        {
            npair++; //计算不同对子的个数
            setv(2,i);
        }
    }
    if(npair!=0 && n==2) //打一对就可以打完
        setall(EMPTY_HAND);

    int ntri=0; //三张一样的对数
    for(int i=1;i<=15;i++) //计算第三,四,五种出牌类型的权重
    {
        if(card[i]>=3) //有三张以上
        {
            ntri++;
            setv(3,i);
            if(n-card[i]>=1) //出了三张,至少还有一张是不同的
                setv(4,i);
            if(npair>=2) //出了这三张,还可以出一对,因为3这本身三张也算是一对,所以要大于等于2
                setv(5,i);
        }
    }
    if((n==3 && ntri==1) || (n==5 && ntri==3 && npair==2)) //一次出三张就打完了,或者 一次出5张 ,就是三张带一对
        setall(EMPTY_HAND);
    if(ntri==1 && n==4) setall(EMPTY_HAND); //三张带一条
    int ntrio=0; //四张一起的
    for(int i=1;i<=15;i++)
    {
        if(card[i]==4)
        {
            ntrio++;
            setall(Bomb+i); //更新全部出牌类型炸弹,因为炸弹是可以当成任意出牌类型的
            if(n>=6)
                setv(6,i); //出四张 ,加两张,注意这两张可以是不同的
        }
    }
    if((n==4 && ntrio==1) || (ntrio==1 && n==6)) //出四张带一张,还有,带两张
        setall(EMPTY_HAND);
}
int main()
{
//    freopen("in","r",stdin);
//    freopen("out","w",stdout);
    int T;
    scanf("%d", &T);
    while(T--)
    {
        string a,b;
        cin>>a>>b;
        memset(apro,0,sizeof(apro));
        memset(bpro,0,sizeof(bpro));
        is_first=true; //第一个先手
        pro=apro;
        cal(a);
        is_first=false;//不是先手
        pro=bpro;
        cal(b);
        bool win=false;
        for(int i=1; i<=6; i++)
        {
            if(apro[i] && apro[i]>=bpro[i]) //某一种出牌的权重大于第二个人就可以赢了
            {
                win=true;
                break;
            }
        }
        puts(win?"Yes":"No");
    }
    return 0;
}

抱歉!评论已关闭.