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
*/


posted @ 2017-11-01 15:35  mofushaohua  阅读(195)  评论(0编辑  收藏  举报