2012-0018

从 Trac 迁移的文章

这是从旧校内 Wiki 迁移的文章,可能存在一些样式问题,您可以向 memset0 反馈。

原文章内容如下:

题意是给13张麻将牌,然后给定胡牌的规则4组3张牌+1组对子。然后问有哪些牌你拿到就能胡牌。
具体做法是这样:
首先枚举你拿到的那一张牌,然后枚举哪一对牌作为对子,接下来检验剩下的12张牌能否凑成4*3的形式。
检验的时候,首先看Honor tiles(z),因为Honor tiles没有顺子,所以只能凑成3张一样的,故每一种Honor tile只能是1张或3张。
然后检验筒(p)、条(s)、万(m)每一种。
对于其中的一种,从1往9扫,用b[i]表示这一种花色的牌数字为i的牌的个数。
对于当前扫到的i,如果b[i]>=3,b[i]=b[i]-3.即将i凑成三张一样的形式。
判断b[i]>=3之后,如果b[i]>0(即原本b[i]=1、2或b[i]=4的情况),并且i<=7,b[i+1]>=b[i],b[i+2]<=b[i](即能够利用<i,i+1,i+2>的顺子把i分配完),则
b[i+2]=b[i+2]-b[i],b[i+1]=b[i+1]-b[i],b[i]=0(因为使用来b[i]数量的<i,i+1,i+2>,否则的话就没有办法形成一种胡牌的形式。
下面证明这一种贪心式构成法的正确性:
假设目前构成到i,1~i-1都已经组成3张的形式来,如果b[i]<3,则只能通过顺子的形式分配i,如果b[i]>=3,形成一个<i,i,i>的3张形式必然是最优的。因为i只能通过顺子或三张一样的形式来组成胡牌形式,如果使用顺子,需要等数量的i+1和i+2(因为1~i-1已经分配完),那么b[i+1]>=b[i]>=3,b[i+2]>=b[i]>=3,因此如果在b[i]>=3的时候可以用顺子<i,i+1,i+2>将i分配,必然可以将其中三个<i,i+1,i+2>用<i,i,i><i+1,i+1,i+1><i+2,i+2,i+2>的形式展现,因此b[i]>=3的时候组成<i,i,i>情况不会变差,故这种构成方法是正确的。

下面附上我的程序:
#include <cstdio>
#include <cstring>
#include <map>
#define D if(0)
using namespace std;

map<char,int> nu;
char ch[5];
int a[10][5],b[10][5];
int x[40],y[40],l[5];;
int m;

void add(char o,char p)
{
    int x,y;
    x=o-'0';
    y=nu[p];
    a[x][y]++;
}

bool check()
{   

    for(int i=1;i<=3;i++)
    for(int j=1;j<=9;j++) b[j][i]=a[j][i];
    for(int i=1;i<=3;i++)
    for(int j=1;j<=9;j++)
    {
        if(b[j][i]>=3) b[j][i]-=3;       
        if(b[j][i]>0)
        {
            if(j>7) return false;
            if((b[j+1][i]<b[j][i])||(b[j+2][i]<b[j][i])) return false;
            b[j+1][i]-=b[j][i];
            b[j+2][i]-=b[j][i];
            b[j][i]=0;      
        }
    }
    return true;
}

bool win()
{
    bool ok=false;
    for(int i=1;i<=4;i++)
    for(int j=1;j<=l[i];j++)
    if(a[j][i]>=2)
    {
        a[j][i]-=2;
        ok=true;
        for(int k=1;k<=7;k++)
        if((a[k][4]!=0)&&(a[k][4]!=3)) ok=false;
        if(ok) ok=check();
        a[j][i]+=2; 
        if(ok) return true;
    }
    return false;
}

int main()
{
    nu['m']=1;
    nu['p']=2;
    nu['s']=3;
    nu['z']=4;
    ch[1]='m';
    ch[2]='p';
    ch[3]='s';
    ch[4]='z';
    l[1]=9;l[2]=9;l[3]=9;l[4]=7;
    while(scanf("%c%c",&o,&p)!=EOF)
    {
        memset(a,0,sizeof(a));
        add(o,p);
        for(int i=2;i<=13;i++)
        {
            scanf("%c%c",&o,&p);
            add(o,p);
        }
        scanf("%*c");
        m=0;
        for(int i=1;i<=4;i++)
        for(int j=1;j<=l[i];j++)
        if(a[j][i]<4)
        {
            a[j][i]++;
            if(win())
            {
                m++;
                x[m]=j;
                y[m]=i;
            }
            a[j][i]--;
        }
        printf("%d",m);
        if(m>0)
        {
            printf(" ");
            for(int i=1;i<=m;i++)
            printf("%d%c",x[i],ch[y[i]]);
        }
        printf("\n");
    }
    return 0;
}


BY hzwlzrj 

题意是给13张麻将牌,然后给定胡牌的规则4组3张牌+1组对子。然后问有哪些牌你拿到就能胡牌。

具体做法是这样:

首先枚举你拿到的那一张牌,然后枚举哪一对牌作为对子,接下来检验剩下的12张牌能否凑成4*3的形式。

检验的时候,首先看Honor tiles(z),因为Honor tiles没有顺子,所以只能凑成3张一样的,故每一种Honor tile只能是1张或3张。

然后检验筒(p)、条(s)、万(m)每一种。

对于其中的一种,从1往9扫,用b[i]表示这一种花色的牌数字为i的牌的个数。

对于当前扫到的i,如果b[i]>=3,b[i]=b[i]-3.即将i凑成三张一样的形式。

判断b[i]>=3之后,如果b[i]>0(即原本b[i]=1、2或b[i]=4的情况),并且i<=7,b[i+1]>=b[i],b[i+2]<=b[i](即能够利用的顺子把i分配完),则

b[i+2]=b[i+2]-b[i],b[i+1]=b[i+1]-b[i],b[i]=0(因为使用来b[i]数量的,否则的话就没有办法形成一种胡牌的形式。

下面证明这一种贪心式构成法的正确性:

假设目前构成到i,1~i-1都已经组成3张的形式来,如果b[i]<3,则只能通过顺子的形式分配i,如果b[i]>=3,形成一个的3张形式必然是最优的。因为i只能通过顺子或三张一样的形式来组成胡牌形式,如果使用顺子,需要等数量的i+1和i+2(因为1~i-1已经分配完),那么b[i+1]>=b[i]>=3,b[i+2]>=b[i]>=3,因此如果在b[i]>=3的时候可以用顺子将i分配,必然可以将其中三个的形式展现,因此b[i]>=3的时候组成情况不会变差,故这种构成方法是正确的。

下面附上我的程序:

#include

#include

#include

#define D if(0)

using namespace std;

map nu;

char ch[5];

int a[10][5],b[10][5];

int x[40],y[40],l[5];;

int m;

void add(char o,char p)

{

int x,y;

x=o-'0';

y=nu[p];

a[x][y]++;

}

bool check()

{

for(int i=1;i<=3;i++)

for(int j=1;j<=9;j++) b[j][i]=a[j][i];

for(int i=1;i<=3;i++)

for(int j=1;j<=9;j++)

{

if(b[j][i]>=3) b[j][i]-=3;

if(b[j][i]>0)

{

if(j>7) return false;

if((b[j+1][i]

b[j+1][i]-=b[j][i];

b[j+2][i]-=b[j][i];

b[j][i]=0;

}

}

return true;

}

bool win()

{

bool ok=false;

for(int i=1;i<=4;i++)

for(int j=1;j<=l[i];j++)

if(a[j][i]>=2)

{

a[j][i]-=2;

ok=true;

for(int k=1;k<=7;k++)

if((a[k][4]!=0)&&(a[k][4]!=3)) ok=false;

if(ok) ok=check();

a[j][i]+=2;

if(ok) return true;

}

return false;

}

int main()

{

nu['m']=1;

nu['p']=2;

nu['s']=3;

nu['z']=4;

ch[1]='m';

ch[2]='p';

ch[3]='s';

ch[4]='z';

l[1]=9;l[2]=9;l[3]=9;l[4]=7;

while(scanf("%c%c",&o,&p)!=EOF)

{

memset(a,0,sizeof(a));

add(o,p);

for(int i=2;i<=13;i++)

{

scanf("%c%c",&o,&p);

add(o,p);

}

scanf("%*c");

m=0;

for(int i=1;i<=4;i++)

for(int j=1;j<=l[i];j++)

if(a[j][i]<4)

{

a[j][i]++;

if(win())

{

m++;

x[m]=j;

y[m]=i;

}

a[j][i]--;

}

printf("%d",m);

if(m>0)

{

printf(" ");

for(int i=1;i<=m;i++)

printf("%d%c",x[i],ch[y[i]]);

}

printf("\n");

}

return 0;

}

BY hzwlzrj