UVA - 127 Accordian Patience
/* 题目链接: https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=63 这题也是比较考思维,好像没什么特别的技巧和知识点,但是自己想的时候,怎么也想不到用 vector< vector<card> > 处理的这种方式... 可能STL学的还不够熟练吧... 参考了别人的题解和思路,别人的思路真是太巧妙了,引用一句评价: “每次只输入一张牌,马上向前查看是否能匹配,而不是一副牌都分配好后再来插入和删除,效率高,实现效果好。” 总结一下:值得重做的题,可以极大加深对STL的理解。 而且,如果一开始不看题解,而是先自己想该怎么实现,很久没有想明白以后,再去看题解,更能发现,这个博主的思路真的非常非常巧妙,让人想要拊掌称叹 */
/* 参考借鉴思路于: http://blog.csdn.net/bb2b2bbb/article/details/26484267 */ #include <iostream> #include <vector> #define rep(i, j, n) for (int i = j; i < n; i++) using namespace std; struct card { char number, color; card(char n = ' ', char c = ' '):number(n), color(c) { } }; vector<vector<card> > vec; int deal(card c, int size) { //该函数返回牌插入的位置,因为牌的规则包含各种 match 和 移动 的情况的处理 int tp; //temp while (size > 0) //开始判断能否 match 、能否移动,可左移一格也可左移格时,移动3格,所以先判断3格的情况 { if (size >= 3) { tp = (int)vec[size - 3].size() - 1; //最顶端的牌才能参与 match 或者参与移动,tp是最顶端的牌的下标 card& c0 = vec[size - 3][tp]; if ( c0.number == c.number || c0.color == c.color ) { size -= 3; continue; } } if (size >= 1) { tp = (int)vec[size - 1].size() - 1; //最顶端的牌才能参与 match 或者参与移动,tp是最顶端的牌的下标 card& c0 = vec[size - 1][tp]; if ( c0.number == c.number || c0.color == c.color ) { size -= 1; continue; } } break; //如果有移动,则可不断继续判断,是否仍满足移动的条件,直到不能移动为止,不能移动时,跳出循环 } return size; //返回该牌最终放置的位置 } void solve() { char input[3]; while (cin >> input && input[0] != '#') { int num = 52; while (num--) { card c(input[0], input[1]); int pos = deal(c, (int)vec.size()); if (pos == (int)vec.size()) //卡片没有移动,直接插入 { vector<card> v(1, c); vec.push_back(v); } else //卡片能移动,pos是卡片最终放置的位置 { vec[pos].push_back(c); Again: rep( i, pos + 1, (int)vec.size() ) //每次一旦有卡片插入,则跳到卡片插入位置之后,找其后还有没有可插入的卡片;所以相当于是进入了一个全新的循环,和原来的循环完全不同了。这点用标号来实现最为简洁,所以尽管不建议用goto,但在这里,我想不到比goto更好的代替方式了 { card d = vec[i][(int)vec[i].size() - 1]; int loc = deal(d, i); //loc::location,这里的处理挺有意思,把已经遍历过的牌堆的后一个牌堆,这个牌堆上的最顶部的牌,当作是要插入已经排好的那些牌堆中,看看这张牌能不能移动 if ( loc != i ) { vec[loc].push_back(d); //把这种牌插入该移动到的位置 vec[i].pop_back(); //把这张牌从原来的堆顶移除 if (vec[i].empty()) vec.erase(vec.begin() + i); //如果删完以后,牌堆空了,整个牌堆移除 pos = loc; //更新下已经拍好的牌堆的数目,使得从已经排好的牌堆的后面,再来找有没有可以移动的牌堆 goto Again; } } } if (!num) break; //这组的52张牌全部发完,开始处理下组数据 cin >> input; //52张没发完,接着发牌 } cout << (int)vec.size() << (vec.size() != 1 ? " piles remaining: ":" pile remaining: "); rep(i, 0, (int)vec.size()) { if ( i != vec.size() - 1 ) cout << vec[i].size() << " "; else cout << vec[i].size() << endl; } vec.clear(); } } int main() { solve(); return 0; }
/* 查阅的其他资料: http://mikixiyou.iteye.com/blog/1754413 http://www.cnblogs.com/freeopen/p/5482962.html http://blog.csdn.net/glrh123/article/details/45542549 */