题目大意
把52张牌从左到右排好,每张牌自成一个牌堆。当某张堆牌的堆顶牌与它左边第一堆或者左边第三堆的堆顶牌匹配时(花色或者点数相同)时,就把这张堆顶牌移到与它匹配的那堆牌上面。
移动时要满足的规则:
- 当牌堆之间出现空隙时要立刻把右边的所有牌堆左移一格来填补空隙。
- 如果有多张牌可以移动,先移动最左边的那张牌。
- 如果即可以移一格也可以移三格时,移动三格。
按顺序输入52张牌,输出最后牌堆数以及各牌堆的牌数。
分析
先分析移动牌的过程,很明显后移进来的牌会作为这堆牌的堆顶,也就是后来者居上,我们可以用堆栈来模拟牌堆。
不会堆栈的话看这里:连接传送门~~
我们再来考虑移动牌过后,出现空隙的情况,就也是我们选用的存储容器可以自由的删除中间的元素,这里我们可以用动态数组vector。
动态数组 vector
定义: vector<数据类型> 名字; eg: vector< int > v;
插入元素:v.push_back(数据); eg: v.push_back(5);
删除元素: v.erase(地址); eg: v.erase(v.begin() + x); 如果要删除第一个元素,x = 0 …
计算大小: v.size(); 返回 v 中元素的个数。
判断是否为空:v. empty(); 如果 v 中没有元素返回真,否则返回假。 时间复杂度较小。
每张牌的属性有两个,花色和数字。这里我们用 pair 来存,也可以自己写结构体来存。
具体实现看代码注释。
AC代码
#include <bits/stdc++.h>
#include <vector>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
#include <map>
#include <set>
#include <stack>
#define ll long long
#define chushi(a, b) memset(a, b, sizeof(a))
#define endl "\n"
const double eps = 1e-8;
const ll INF=0x3f3f3f3f;
const int mod = 20000311;
const int maxn = 1e6 + 5;
const int N=1005;
using namespace std;
vector<stack<pair<char, char> > > v;
// 动态数组的每个元素是一个堆栈,也就是一堆牌
// 堆栈的每个元素是一个 pair, 也可以写结构体
// 这里我们相当于用动态数组模拟链表
void creat(char a, char b){
stack<pair<char, char> > s; // 创建堆栈
s.push({a, b}); // 把牌放到这个堆栈里边
v.push_back(s); // 把这个堆栈放到动态数组中
}
bool move(int x){ // 移动牌成功移动就返回 true,否则返回 false
char a = v[x].top().first, b = v[x].top().second;
if(x >= 3){ // 优先查看左边第三堆
char aa = v[x-3].top().first, bb = v[x-3].top().second;
if(aa == a || bb == b){ // 匹配
v[x].pop(); // 从 x 中拿出来
if(v[x].empty()) v.erase(v.begin() + x); // 补空隙
v[x-3].push({a, b}); // 放到 x -3 中
return true; // 返回 true
}
}
if(x >= 1){ // 这里查看左边第一堆,同上
char aa = v[x-1].top().first, bb = v[x-1].top().second;
if(aa == a || bb == b){
v[x].pop();
if(v[x].empty()) v.erase(v.begin() + x);
v[x-1].push({a, b});
return true;
}
}
return false; // 如果没有移动就返回false
}
void pr(){ // 输出
int res = v.size();
if(res > 1) cout << res << " piles remaining:"; // 细节
else cout << res << " pile remaining:";
for(int i = 0; i < res; i++) cout << " " << v[i].size();
cout << endl;
}
int main(){
string s;
while(1){
for(int i = 1; i <= 26; i++){ // 输入
cin >> s;
if(s[0] == '#') break;
creat(s[0], s[1]); // 创建一堆牌
}
if(s[0] == '#') break; // 结束
for(int i = 1; i <= 26; i++){
cin >> s;
creat(s[0], s[1]);
}
while(1){ // 开始移动牌
int f = 1;
int n = v.size();
for(int i = 0; i < n; i++){
if(move(i)){ // 每次从左到右依次尝试取移动
f = 0;
break; // 移动成功就结束,重新扫描
}
}
if(f) break; // 如果某次扫描没移动牌,说明没有可以移动的牌了
}
pr(); // 输出
v.clear(); // 清空动态数组
}
return 0;
}
最后讲一下什么是链表:
类似这样,通过指针连接在一起的非连续的元素叫指针。
head 指针一般作为头节点指向第一个元素所在的位置。
每个元素一般都是一个结构体,有两大部分:
- 数据
- next 指针,指向它的下一个元素。如果没有下一个元素的话,就指向空。
typedef struct Node{
int data;
Node *next;
} node;
链表属于一种数据结构在需要考虑空间大小的问题上有独特的优点,详细的解释看这篇博客吧 传送门
在竞赛中,一般不需要考虑空间问题,所以一般用动态数组可以模拟链表。