文章目录
P3613 寄包柜(数组与字典)
#include <iostream>
#include <map>
using namespace std;
//using boxes = map<int,map<int,int>>;
int main(int argc, const char * argv[]) {
int n,q;
cin >> n >> q;
map<int,int>* boxes = new map<int,int>[n];
while(q--){
int choice,i,j;
cin >> choice >> i >> j;
i -= 1;
j -= 1;
if(choice == 1){
int object;
cin >> object;
boxes[i][j] = object;
}
else{
cout << boxes[i][j] << endl;
}
}
delete [] boxes;
return 0;
}
因为不知道箱子有多少个,所以直接构建箱子序号和内容的映射关系即可,用map最好了,占用量也不大。
P1446 后缀表达式(栈)
#include <iostream>
#include <stack>
#include <string>
#include <vector>
using namespace std;
int cal(int l, int r, char c){
switch(c){
case '+': {return l+r;}
case '-': {return l-r;}
case '*': {return l*r;}
case '/': {return l/r;}
default: {throw __cpp_exceptions;}
}
}
int main(int argc, const char * argv[]) {
string seq;
cin >> seq;
stack<int,vector<int>> q;
int number = 0;
for(auto&s:seq){
if(s == '.'){
q.push(number);
number = 0;
}
else if (s == '@'){break;}
else if(s == '+' || s == '-' || s == '*' || s == '/'){
int r = q.top();
q.pop();
int l = q.top();
q.pop();
q.push(cal(l,r,s));
}
else{
number = number * 10 + s - '0';
}
}
cout << q.top() << endl;
return 0;
}
这里需要注意把两个连续的字符合并成一个数字的思路。
P1996 约瑟夫问题(队列)
#include <iostream>
#include <queue>
#include <deque>
using namespace std;
int main(int argc, const char * argv[]) {
int n,m,id;
cin >> n >> m;
queue<int,deque<int>> q;
for(int i=1; i<=n; i++){q.push(i);}
while(!q.empty()){
for(int i=1; i<m; i++){
id = q.front();
q.pop();
q.push(id);
}
cout << q.front() << ' ';
q.pop();
}
return 0;
}
P1160 队列安排
#include <iostream>
using namespace std;
struct node{
int element = 0;
node* prev = NULL;
node* next = NULL;
};
int main(int argc, const char * argv[]) {
int n;
cin >> n;
node *seq = new node [n+2];
// 初始化;
seq[0].next = &seq[1];
seq[1].element = 1;
seq[1].prev = &seq[0];
seq[1].next = &seq[n+1];
seq[n+1].prev = &seq[1];
int k,p;
for(int i=2; i<=n; i++){
cin >> k >> p;
if(p==0){
seq[i].element = i;
seq[i].prev = seq[k].prev;
seq[i].next = &seq[k];
seq[k].prev->next = &seq[i];
seq[k].prev = &seq[i];
}
else{
seq[i].element = i;
seq[i].next = seq[k].next;
seq[i].prev = &seq[k];
seq[k].next->prev = &seq[i];
seq[k].next = &seq[i];
}
}
int m,order;
cin >> m;
for(int i=0; i<m; i++){
cin >> order;
if(seq[order].element == 0)
continue;
else{
seq[order].prev->next = seq[order].next;
seq[order].next->prev = seq[order].prev;
seq[order].next = NULL;
seq[order].prev = NULL;
seq[order].element = 0;
}
}
node student = *seq[0].next;
do{
cout << student.element << ' ';
student = *student.next;
}while(student.element!=0);
return 0;
}
本题的核心关键是插入,自然而然想到链表这种插入贼方便的数据结构。
于是乎,我们只要找到基准点,然后执行队列的插入操作即可。
因为可以选择插在左边还是右边,自然而然我们会想到双端队列。
为了维护每一个插入的操作是一致的,所以选择加入头尾哨兵也是普遍的操作。
难点在于,我们如何立马找到这个基准点?
不妨想一想链表/树结构的并查集,额外找个数组来存储这些点就完事了,每次根据输入立马索引到那个位置。
P2058 海港(队列与字典)
#include <iostream>
#include <map>
#include <utility>
#include <queue>
#include <vector>
using namespace std;
map<int,int> nations; // 维护国籍及其对应人数
queue<pair<int,vector<int>>> seq; // 维护24h船只
void out_boat(vector<int> &boat){
for(auto &nation:boat){
nations[nation]--;
if(nations[nation]==0)
nations.erase(nation);
}
}
void in_boat(vector<int> &boat){
for(auto &nation:boat){
try{
nations[nation]++;}
catch(...){
nations[nation] = 1;}
}
}
int main(int argc, const char * argv[]) {
int n,t,k,visitor;
cin >> n;
while(n--){
cin >> t >> k;
vector<int> boat;
while(k--){
cin >> visitor;
boat.push_back(visitor);
}
seq.push({t,boat});
in_boat(boat);
while(seq.front().first<=t-86400){
out_boat(seq.front().second);
seq.pop();
}
cout << nations.size() << endl;
}
return 0;
}
本来的思路是每进一艘船,然后在24小时内遍历,一艘一艘得算。讲得夸张一点,n艘船,24小时内有m艘船,每艘船平均t个人,那么也是 O ( m n t ) O(mnt) O(mnt)这样一个夸张的时间复杂度。
结合前缀和的思想,希望有一个实时更新的数据来记录国籍情况,或者说,我们把目光放到国籍/人身上也是不错的选择。那么n艘船,进来时要对t个人登记,假设有p艘船出去,那么时间复杂度就是 O ( n ( p + 1 ) t ) O(n(p+1)t) O(n(p+1)t)。
通常情况下, p + 1 < m p+1\lt m p+1<m所以显然这个情况更优秀一些。
P1241 括号序列(栈)
`
#include <iostream>
#include <stack>
#include <deque>
#include <string>
struct node{
char element;
bool matched=false;
};
using namespace std;
int main(int argc, const char * argv[]) {
stack<node*,deque<node*>> q;
string seq;
cin >> seq;
node *nodes = new node[seq.size()];
for(int i=0; i<seq.size(); i++){
nodes[i].element = seq[i];
if(seq[i] == '(' || seq[i] == '[')
q.push(&nodes[i]);
else{
if(!q.empty()&&((q.top()->element == '(' && seq[i] == ')') || (q.top()->element == '[' && seq[i] == ']'))){
q.top()->matched = true;
nodes[i].matched = true;
q.pop();
}
}
}
for(int i=0; i<seq.size(); i++){
if(!nodes[i].matched){
if(nodes[i].element == ')')
cout << '(';
else if(nodes[i].element == ']')
cout << '[';
}
cout << nodes[i].element;
if(!nodes[i].matched){
if(nodes[i].element == '(')
cout << ')';
else if(nodes[i].element == '[')
cout << ']';
}
}
return 0;
}
括号匹配问题,典型的栈。然而匹配规则存在歧义,详细解释可以参照这里。就是找最近的没有被匹配过的左括号匹配,匹配上了就双双标记,没匹配上就溜溜球。
P4387 验证栈序列(栈)
#include<iostream>
#include<stack>
using namespace std;
int main()
{
int k;
cin >> k;
while(k--){
stack<int> s;
int n;
cin >> n;
int pushed[n],popped[n];
for(int i=0; i<n; i++)
cin >> pushed[i];
for(int i=0; i<n; i++)
cin >> popped[i];
// 总共就进行这n轮,n轮结束后,栈里面还有元素,那就说明不可以
int sum = 0;
for(int i=0; i<n; i++){
s.push(pushed[i]);
// 栈作为一个配对中转站,一旦有元素走了之后,要接着看看还有没有可以跟着走的
// 否则,被新的元素插入后,就走不了了
while(s.top() == popped[sum]){
s.pop();
sum++;
if(s.empty())
break;
}
}
if(s.empty())
cout << "Yes" << endl;
else
cout << "No" << endl;
}
return 0;//
}
此题的第一个难点是题意,这里需要考虑栈的任意合理输出。
本质和之前的火车重排问题类似,这里抽象出来是:一边要逐个进来,一边要配对出去的双序列问题,所以一旦配对成功一个要看看接下来的能不能走。