Re: あなたのスキルで飯は食えるか? 史上最大のコーディングスキル判定
あなたのスキルで飯は食えるか? 史上最大のコーディングスキル判定
Twitterでこんなものを見かけたので解いてみました。
以下ネタバレ注意。
Code
#include <iostream> using namespace std; enum{ N=12, S, K, H }; struct tuple{ int type, n; }; bool operator<(const tuple &a, const tuple &b){ if(a.type==b.type) return a.n < b.n; return a.type<b.type; } ostream& operator<<(ostream &os, const tuple &t){ if(t.type == S) return os << '(' << t.n << t.n+1 << t.n+2 << ')'; else if(t.type == K) return os << '(' << t.n << t.n << t.n << ')'; else return os << '(' << t.n << t.n << ')'; } void rec(int v[], int n, tuple t[], int tn){ if(n==1){ // tanki for(int i=1;i<=9;++i) if(v[i]) cout << t[0] << t[1] << t[2] << t[3] << '[' << i << ']' << endl; } if(n==2){ for(int i=1;i<=9;++i){ // penchan, ryanmen if(v[i] && v[i+1]) cout << t[0] << t[1] << t[2] << t[3] << '[' << i << i+1 << ']' << endl; // kanchan if(v[i] && v[i+2]) cout << t[0] << t[1] << t[2] << t[3] << '[' << i << i+2 << ']' << endl; // shabo if(v[i]>=2) cout << t[0] << t[1] << t[2] << t[3] << '[' << i << i << ']' << endl; } } for(int i=1;i<=9;i++){ if(v[i]>=3){ v[i]-=3; t[tn]=(tuple){K, i}; if(!(tn && t[tn]<t[tn-1])) rec(v, n-3, t, tn+1); v[i]+=3; } if(v[i] && v[i+1] && v[i+2]){ v[i]--; v[i+1]--; v[i+2]--; t[tn]=(tuple){S, i}; if(!(tn && t[tn]<t[tn-1])) rec(v, n-3, t, tn+1); v[i]++; v[i+1]++; v[i+2]++; } } if(n==4){ // to non-tanki for(int i=1;i<=9;i++){ if(v[i]>=2){ v[i]-=2; t[tn]=(tuple){H, i}; rec(v, n-2, t, tn+1); v[i]+=2; } } } } int main(){ string str; while(cin >> str){ int v[N]; tuple t[4]; for(int i=0;i<N;++i) v[i]=0; for(int i=0;i<str.length();++i) v[str[i]-'0']++; //for(int i=0;i<N;++i) cout << v[i]; cout << endl; rec(v,13,t,0); cout << endl; } return 0; }
Output for sample input
(111)(222)(888)(99)[45](123)(123)(555)(99)[67]
(123)(123)(567)(55)[99]
(123)(123)(567)(99)[55](111)(222)(333)(555)[9]
(123)(123)(123)(555)[9](123)(234)(888)(999)[4]
(123)(888)(999)(44)[23]
(234)(234)(888)(999)[1](123)(456)(789)(11)[99]
(123)(456)(789)(99)[11]
(123)(456)(999)(11)[78]
(123)(678)(999)(11)[45]
(234)(567)(111)(999)[8]
(234)(567)(111)(99)[89]
(234)(678)(111)(999)[5]
(234)(789)(111)(99)[56]
(345)(678)(111)(999)[2]
(345)(678)(999)(11)[12]
(456)(789)(111)(99)[23]
Notes
- 所要時間は約40分。
- 方針はただのバックトラック。
- 残り1枚なら 単騎待ち
- 残り2枚なら ペンチャンカンチャンシャボ待ち
- 残り4枚なら 面子かアタマを探す
- それ以外なら 面子を探す
- 面子3つをバックトラックで探索して、残り4牌の処理は愚直に書いた方が簡単だったかもしれない。
- 実は待ちを探す問題を解くのは初めてではない。
- 工夫した点といえば、入力の持ち方ぐらいか。
- そのまま持つのではなく、 v[i] = 牌 i の所持枚数 の形で持つ。
- 範囲外アクセスのチェックをサボるために、配列はちょっと長めにとってみるとか。
- 何度見直しても汚い。