题意:
移牌游戏,按以下规则:
1. 从左向右, 考虑每张牌, 若某张牌与其左边第1张或左边第3张是同一个牌号或花色, 则就把这张牌移到左边的匹配牌上方, 并继续向左边移动, 直至左边第1张或第3张都不是符合的牌为止.
2. 如果同时有两张以上的牌可以移动, 则总是移动最左边的牌.
3. 如果既可以移到左边第1张也可以移到左边第3张, 则总是移到左边第3张上面.
4. 如果某一列被移为空, 则把右边的牌合并过来.
5. 每次只取一列中最顶上的牌进行比较.
当所有牌都不能移动时, 从左到右输出每一列包含的张数.
思路:
1. 使用 list 存放每一列, 再使用 vector 存放所有的列.
2. 每次比较时, 在 vector 里从左到右依次取出一列, 比较其最顶的牌与其左边第1张、第3张牌是否为同一分组,若是同一分组, 则按"题意"规则 1, 3 进行移动.
3. 注意, 当一张牌移动完毕后, 应该从它移动到的牌的下一张开始重新循环, 而不是移动时的牌的下一张, 否则无法满足"题意"中的规则 2.
要点:
1. vector 的 erase 会自动把后(右)边的元素补上来, 正好满足这里的需要.
2. list 通过 push_front, push_back, pop_front, pop_back 来操纵其首尾元素.
题目:
http://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&category=103&page=show_problem&problem=63
代码:
# include <iostream> # include <string> # include <cstdio> # include <cstring> # include <vector> # include <algorithm> # include <cctype> # include <iterator> # include <assert.h> # include <list> using namespace std; // 1. 如果有两张牌可以移动,则总是移动最左的那张,即从左到右逐张牌进行处理 // 2. 如果一张牌 match 其左边第1张或第3张牌,就把它移过去 // (要先移,即类似冒泡,而不是选择法),往复至不能移为止 // 3. 若同时满足两种情况,则移到最左边 // 4. match 是指两张牌在相同的 suit 或 rank // 5. 只取第一张牌进行比较 // 6. 若出现某一列为空,则把它右边的列往左移 (vector earase 会自动做) // 判断两张牌是否 match, 即判断其对应字符是否相等 bool isMatch(const string& str1, const string& str2) { assert(str1.size() == 2 && str2.size() == 2); return (str1[0] == str2[0] || str1[1] == str2[1]); } // 判断在 i 和 j 处的第一张牌是否 match bool isMatch(const vector< list<string>* > cards, int i, int j) { if (!(i>=0 && i<cards.size() && j>=0 && j<cards.size())) return false; list<string>::const_iterator itI, itJ; itI = cards[i]->begin(); itJ = cards[j]->begin(); return isMatch(*itI, *itJ); } // 将 from 处的 card 移到 to 处 // 返回值表示 move 后当前是否为空 bool moveCard(vector< list<string>* >& cards, int from, int to) { list<string>* pileFrom = cards[from]; list<string>* pileTo = cards[to]; list<string>::iterator it = pileFrom->begin(); pileTo->push_front(*it); pileFrom->pop_front(); return pileFrom->empty(); } // 从 i 开始向右找到其能 match 到的点,直至最左端, 若没有,则返回 -1 int findMatch(const vector< list<string>* >& cards, int i) { if (isMatch(cards, i, i-3)) return i-3; if (isMatch(cards, i, i-1)) return i-1; return -1; } // 处理当前所有牌 void dealCards(vector< list<string>* >& cards) { while (true) { int i=0; bool needMove = false; while (i<cards.size()) { int to = findMatch(cards, i); bool needShrink = false; if (to != -1) { needMove = true; needShrink = moveCard(cards, i, to); } if (needShrink) { delete cards[i]; // 先把这块的空间释放掉 // erase 会自动把后面的合并上来 cards.erase(cards.begin()+i); } // 如果移动了,则从移动后的位置开始重新计算, // 这样才能保证有两个可以移动时,移动的是最左边的那个 if (to != -1) { i = to; continue; } i++; } // 如果全部都不需要 move 了,表示游戏结束 if (!needMove) break; } } int main(int argc, char const *argv[]) { #ifndef ONLINE_JUDGE freopen("127_i.txt", "r", stdin); freopen("127_o.txt", "w", stdout); #endif string card; while (!cin.eof()) { cin >> card; if (card == "#") break; // vector 存放所有的牌, 每一列都用一个 list 来存放 vector< list<string>* > cards; list<string>* pile; pile = new list<string>; pile->push_front(card); cards.push_back(pile); // 读入后 51 张牌 for (int i=0; i<51; i++) { cin >> card; pile = new list<string>; pile->push_front(card); cards.push_back(pile); } dealCards(cards); // 注意 pile 有单复数之分 // printf("%d piles remaining: ", int(cards.size())); if (cards.size() > 1) { printf("%d piles remaining:", int(cards.size())); } else { printf("%d pile remaining:", int(cards.size())); } // 注意这里的空格是打在 size 前面的,不是打在后面 // 否则会报 Presentation error for (int i=0; i<cards.size(); i++) { cout << " " << cards[i]->size(); delete cards[i]; // 输出完了,要释放空间 } cout << endl; } return 0; }
环境: C++ 4.5.3 - GNU C++ Compiler with options: -lm -lcrypt -O2 -pipe -DONLINE_JUDGE