makeplex presents 史上最強のプログラマー検定試験提出回答
- 深さ優先探索
- 初めに待ちを固定して手に加えた状態から探索開始
- 深さ0〜3では順子・刻子を全数検査
- 深さ4では頭を全数検査
- 解となる文字列の生成ではどの順子・刻子・頭に待ちがあるかを固定して生成
- 重複した解が出力されないようsetを使用
- 途中状態をメモしたところ違うパスで同じ状況になりうるパターンが有ったためメモを破棄した
- 問題ページに掲載されている入出力データは試した
- それ以外は試していない
- 七面子は対応していない
#include <algorithm> #include <cassert> #include <cfloat> #include <climits> #include <cmath> #include <complex> #include <cstdio> #include <cstdlib> #include <cstring> #include <ctime> #include <fstream> #include <functional> #include <iomanip> #include <iostream> #include <list> #include <map> #include <memory> #include <numeric> #include <queue> #include <set> #include <sstream> #include <stack> #include <string> #include <utility> #include <vector> using namespace std; static const double EPS = 1e-8; static const double PI = 4.0 * atan(1.0); static const double PI2 = 8.0 * atan(1.0); typedef long long ll; vector<vector<int> > path; set<string> answers; // 答えを登録する void regist(int waiting) { vector<vector<int> > hand = path; sort(hand.begin(), hand.end()); for (int headIndex = 0; headIndex < hand.size(); ++headIndex) { // 待ちを含む順子・刻子・アタマを探す if (find(hand[headIndex].begin(), hand[headIndex].end(), waiting) == hand[headIndex].end()) { continue; } // 待ちを含まない順子・刻子・アタマを文字列化する string s; for (int index = 0; index < hand.size(); ++index){ if (index == headIndex) { continue; } s += '('; for (vector<int>::iterator it = hand[index].begin(), itEnd = hand[index].end(); it != itEnd; ++it) { s += (char)(*it + '0'); } s += ')'; } // 待ちを含む順子・刻子・アタマを文字列化する bool firstWaiting = true; s += '['; for (vector<int>::iterator it = hand[headIndex].begin(), itEnd = hand[headIndex].end(); it != itEnd; ++it) { if (firstWaiting && *it == waiting) { firstWaiting = false; continue; } s += (char)(*it + '0'); } s += ']'; // 答えとなる文字列を登録する // 重複は自動的に弾かれる answers.insert(s); } } //// メモ付き探索のキーを生成するためのハッシュ生成関数 //ll makeHash(int cards[]) { // ll hash = -1; // for (int i = 1; i <= 9; ++i) { // hash *= 137; // hash += cards[i]; // } // return hash; //} // //set<ll> memo; // メモ付き探索 // ・・・にしようとしたら同じ状態で違う解というパターンが // 有ったので泣く泣く普通の探索に・・・ void dfs(int depth, int waiting, int cards[]) { //const ll hash = makeHash(cards); //if (memo.count(hash)) { // return; //} //memo.insert(hash); // 頭の探索 if (depth == 4) { for (int head = 1; head <= 9; ++head) { if (cards[head] == 2) { vector<int> heads(2, head); path.push_back(heads); regist(waiting); path.pop_back(); break; } } return; } // 順子の探索 for (int start = 1; start <= 7; ++start) { if (cards[start] && cards[start + 1] && cards[start + 2]) { vector<int> triple; triple.push_back(start); triple.push_back(start + 1); triple.push_back(start + 2); path.push_back(triple); --cards[start]; --cards[start + 1]; --cards[start + 2]; dfs(depth + 1, waiting, cards); ++cards[start]; ++cards[start + 1]; ++cards[start + 2]; path.pop_back(); } } // 刻子の探索 for (int start = 1; start <= 9; ++start) { if (cards[start] >= 3) { vector<int> triple(3, start); path.push_back(triple); cards[start] -= 3; dfs(depth + 1, waiting, cards); cards[start] += 3; path.pop_back(); } } } int main() { string in; cin >> in; int cards[10]; memset(cards, 0, sizeof(cards)); for (int i = 0; i < in.size(); ++i) { ++cards[in[i] - '0']; } // 待ちを固定して探索 for (int waiting = 1; waiting <= 9; ++waiting) { // すでに四枚ある場合はスキップ if (cards[waiting] == 4) { continue; } ++cards[waiting]; dfs(0, waiting, cards); --cards[waiting]; } for (set<string>::iterator it = answers.begin(); it != answers.end(); ++it) { cout << *it << endl; } }
以上、よろしくお願いいたします。
makeplex salon:あなたのスキルで飯は食えるか? 史上最大のコーディングスキル判定 (1/2) - ITmedia エンタープライズ - http://www.itmedia.co.jp/enterprise/articles/1004/03/news002.html