team2012-E1-mysol-0018

从 Trac 迁移的文章

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

原文章内容如下:

{{{
/* 解题报告 //

打麻将,给出手中的 13 张牌,问再来哪张牌就可以胡
此题的胡指的是 13 + 1 张牌构成 3 + 3 + 3 + 2 的形式
其中 3 是顺子或刻子而 2 是对子

做法是先枚举来哪种花色,将每种花色的牌数 mod 3 后
判断是否有三个 0 和一个 2,没有的话就跳过这种花色
如果有的话,就可以确定对子是哪种花色的了

然后枚举来的是哪张牌,再枚举对子是哪张
加入新拿到的牌并扣除对子后,对现在手上剩下的 12 张牌进行检验

检验的方式是:依次检查各个花色,每个花色从点数最小的牌开始
1. 若能构成刻子,则直接贪心地取出刻子
2. 若这个点数的牌还有剩余,则去试图构成顺子
3. 若试图构成顺子失败,则这 12 张牌不能胡

然后就搞定了

// By 猛犸也钻地 @ 2012.07.22 */
}}}

{{{
#include <cstdio>
#include <vector>
#include <cstring>
using namespace std;

const char* t="mpsz";
int cnt[10][4];
int tmp[10][4];

int gao(){
    memcpy(tmp,cnt,sizeof(tmp));
    int c,x;
    for(c=0;c<4;c++) for(x=1;x<=9;x++){
        if(cnt[x][c]>=3) cnt[x][c]-=3;
        while(cnt[x][c]){
            if(c==3 || x>7 || !cnt[x+1][c] || !cnt[x+2][c]) goto ret;
            cnt[x+0][c]--;
            cnt[x+1][c]--;
            cnt[x+2][c]--;
        }
    }
    ret:
    memcpy(cnt,tmp,sizeof(cnt));
    return c==4;
}

int main(){
    char s[50];
    while(gets(s)){
        vector<int> ans;
        memset(cnt,0,sizeof(cnt));
        for(int i=0;i<13;i++){
            int c=strchr(t,s[i*2+1])-t;
            int x=s[i*2]-'0';
            cnt[x][c]++,cnt[0][c]++;
        }
        for(int c=0;c<4;cnt[0][c++]--){
            int u=0,sum[3]={};
            cnt[0][c]++;
            for(int i=0;i<4;i++) sum[cnt[0][i]%3]++;
            if(sum[0]!=3 || sum[2]!=1) continue;
            while(cnt[0][u]%3!=2) u++;
            for(int x=1;x<=9;x++) if(cnt[x][c]<4){
                cnt[x][c]++;
                for(int e=1;e<=9;e++){
                    cnt[e][u]-=2;
                    int ok=gao();
                    cnt[e][u]+=2;
                    if(ok) ans.push_back(c*10+x),e=10;
                }
                cnt[x][c]--;
            }
        }
        printf("%d",ans.size());
        if(ans.size()) putchar(' ');
        for(size_t i=0;i<ans.size();i++)
            printf("%d%c",ans[i]%10,t[ans[i]/10]);
        puts("");
    }
}
}}}
/* 解题报告 //
打麻将,给出手中的 13 张牌,问再来哪张牌就可以胡
此题的胡指的是 13 + 1 张牌构成 3 + 3 + 3 + 2 的形式
其中 3 是顺子或刻子而 2 是对子
做法是先枚举来哪种花色,将每种花色的牌数 mod 3 后
判断是否有三个 0 和一个 2,没有的话就跳过这种花色
如果有的话,就可以确定对子是哪种花色的了
然后枚举来的是哪张牌,再枚举对子是哪张
加入新拿到的牌并扣除对子后,对现在手上剩下的 12 张牌进行检验
检验的方式是:依次检查各个花色,每个花色从点数最小的牌开始
1. 若能构成刻子,则直接贪心地取出刻子
2. 若这个点数的牌还有剩余,则去试图构成顺子
3. 若试图构成顺子失败,则这 12 张牌不能胡
然后就搞定了
// By 猛犸也钻地 @ 2012.07.22 */
#include <cstdio>
#include <vector>
#include <cstring>
using namespace std;
const char* t="mpsz";
int cnt[10][4];
int tmp[10][4];
int gao(){
    memcpy(tmp,cnt,sizeof(tmp));
    int c,x;
    for(c=0;c<4;c++) for(x=1;x<=9;x++){
        if(cnt[x][c]>=3) cnt[x][c]-=3;
        while(cnt[x][c]){
            if(c==3 || x>7 || !cnt[x+1][c] || !cnt[x+2][c]) goto ret;
            cnt[x+0][c]--;
            cnt[x+1][c]--;
            cnt[x+2][c]--;
        }
    }
    ret:
    memcpy(cnt,tmp,sizeof(cnt));
    return c==4;
}
int main(){
    char s[50];
    while(gets(s)){
        vector<int> ans;
        memset(cnt,0,sizeof(cnt));
        for(int i=0;i<13;i++){
            int c=strchr(t,s[i*2+1])-t;
            int x=s[i*2]-'0';
            cnt[x][c]++,cnt[0][c]++;
        }
        for(int c=0;c<4;cnt[0][c++]--){
            int u=0,sum[3]={};
            cnt[0][c]++;
            for(int i=0;i<4;i++) sum[cnt[0][i]%3]++;
            if(sum[0]!=3 || sum[2]!=1) continue;
            while(cnt[0][u]%3!=2) u++;
            for(int x=1;x<=9;x++) if(cnt[x][c]<4){
                cnt[x][c]++;
                for(int e=1;e<=9;e++){
                    cnt[e][u]-=2;
                    int ok=gao();
                    cnt[e][u]+=2;
                    if(ok) ans.push_back(c*10+x),e=10;
                }
                cnt[x][c]--;
            }
        }
        printf("%d",ans.size());
        if(ans.size()) putchar(' ');
        for(size_t i=0;i<ans.size();i++)
            printf("%d%c",ans[i]%10,t[ans[i]/10]);
        puts("");
    }
}