题目1描述
有n(n≤2×10^6)名同学陆陆续续进入教室。我们知道每名同学的学号(在 1 到10^9之间),按进教室的顺序给出。上课了,老师想知道第 i 个进入教室的同学的学号是什么(最先进入教室的同学i=1),询问次数不超过10^5次。
输入格式
第一行 2 个整数 n 和 m,表示学生个数和询问次数。
第二行 n 个整数,表示按顺序进入教室的学号。
第三行 m 个整数,表示询问第几个进入教室的同学。
输出格式
m 个整数表示答案,用换行隔开。
输入输出样例
输入 #1
10 3
1 9 2 60 8 17 11 4 5 14
1 5 9
输出 #1
1
8
5
两种解题思路:
①动态数组vector
vector<int>IDsmall;
int main()
{
int n,asktimes;
int tmp,tmp2;
cin>>n>>asktimes;
for(int i=0;i<n;i++)
{
cin>>tmp;
IDsmall.push_back(tmp);
}
while(asktimes--)
{
cin>>tmp;
cout<<IDsmall[tmp-1]<<endl;
}
}
②map
map<int,int> ID2SQ;
int main()
{
int n,asktimes;
int tmp;
cin>>n>>asktimes;
for(int i=1;i<=n;i++)
{
cin>>tmp;
ID2SQ[i]=tmp;
}
while(asktimes--)
{
cin>>tmp;
cout<<ID2SQ[tmp]<<endl;
}
}
题目2描述
超市里有n(n<=10^5)个寄包柜。每个寄包柜格子数量不一,第i个寄包柜有 ai(ai≤10^5) 个格子,不过我们并不知道各个ai的值。对于每个寄包柜,格子编号从 1 开始,一直到ai。现在有 q(q≤10^5) 次操作:
1 i j k
:在第i个柜子的第j个格子存入物品k(0≤k≤10^9)。当k=0时说明清空该格子。2 i j
:查询第i个柜子的第j个格子中的物品是什么,保证查询的柜子有存过东西。
已知超市里共计不会超过10^7个寄包格子,ai 是确定然而未知的,但是保证一定不小于该柜子存物品请求的格子编号的最大值。当然也有可能某些寄包柜中一个格子都没有。
输入格式
第一行 2 个整数 n 和 q,寄包柜个数和询问次数。
接下来 q 个整数,表示一次操作。
输出格式
对于查询操作时,输出答案。
输入输出样例
输入 #1
5 4
1 3 10000 114514
1 1 1 1
2 3 10000
2 1 1
输出 #1
114514
1
两种解题思路:
①二维动态数组vector
//by EiJUn
#include <cstdio>
#include <map>
#include <string>
#include <vector>
using namespace std;
const int maxn = 100000;
vector<int> chest;
vector<vector<int> >grid;
int main()
{
int n, q;
scanf("%d%d", &n, &q);
grid.resize(n);
while (q--)
{
int num;
scanf("%d", &num);
if (num == 1)
{
int i, j, k;
scanf("%d%d%d", &i, &j, &k);
if (j > chest.size())
chest.resize(j + 1);
grid[i][j] = k;
}
else
{
int i, j;
scanf("%d%d", &i, &j);
printf("%d\n", grid[i][j]);
}
}
}
②pair+map
#include <iostream>
#include <vector>
#include <map>
#include <utility>
using namespace std;
pair<int,int> xy;
map<pair<int,int>,int> xy2k;
int main()
{
int n;
cin>>n;
int operatetimes;
cin>>operatetimes;
int i,j,k,mode;
while(operatetimes--)
{
cin>>mode;
mode--;
if(mode)
{
cin>>i>>j;
xy=make_pair(i,j);
cout<<xy2k[xy]<<endl;
}
else
{
cin>>i>>j>>k;
xy=make_pair(i,j);
xy2k[xy]=k;
}
}
}
题目3描述
所谓后缀表达式是指这样的一个表达式:式中不再引用括号,运算符号放在两个运算对象之后,所有计算按运算符号出现的顺序,严格地由左而右新进行(不用考虑运算符的优先级)。
如:3*(5–2)+7对应的后缀表达式为:3.5.2.-*7.+@。’@’为表达式的结束符号。‘.’为操作数的结束符号。
输入格式
输入:后缀表达式
输出格式
输出:表达式的值
输入输出样例
输入 #1
3.5.2.-*7.+@
输出 #1
16
解题思路:栈
//by EiJUn
#include <cstdio>
#include <map>
#include <string>
#include <vector>
#include <stack>
using namespace std;
stack<char> ope;
stack<int> num;
int main()
{
char c;
int sum = 0;
while ((c = getchar()) != '@')
{
if (isdigit(c))
{
sum *= 10;
sum = sum + c - '0';
}
if (c == '.')
{
num.push(sum);
sum = 0;
}
if (c == '+')
{
int tmp1=num.top();
num.pop();
int tmp2=num.top();
num.pop();
num.push(tmp2+tmp1);
}
if (c == '-')
{
int tmp1=num.top();
num.pop();
int tmp2=num.top();
num.pop();
num.push(tmp2-tmp1);
}
if (c == '*')
{
int tmp1=num.top();
num.pop();
int tmp2=num.top();
num.pop();
num.push(tmp2*tmp1);
}
if (c == '/')
{
int tmp1=num.top();
num.pop();
int tmp2=num.top();
num.pop();
num.push(tmp2/tmp1);
}
}
printf("%d",num.top());
}
题目4描述
一个学校里老师要将班上N个同学排成一列,同学被编号为1∼N,他采取如下的方法:
先将1号同学安排进队列,这时队列中只有他一个人;
2−N号同学依次入列,编号为i的同学入列方式为:老师指定编号为i的同学站在编号为1∼(i−1)中某位同学(即之前已经入列的同学)的左边或右边;
从队列中去掉M(M<N)个同学,其他同学位置顺序不变。
在所有同学按照上述方法队列排列完毕后,老师想知道从左到右所有同学的编号。
输入格式
第1行为一个正整数N,表示了有N个同学。
第2−N行,第i行包含两个整数k,p,其中k为小于i的正整数,p为0或者1。若p为0,则表示将i号同学插入到k号同学的左边,p为1则表示插入到右边。
第N+1行为一个正整数M,表示去掉的同学数目。
接下来M行,每行一个正整数x,表示将x号同学从队列中移去,如果x号同学已经不在队列中则忽略这一条指令。
输出格式
1行,包含最多N个空格隔开的正整数,表示了队列从左到右所有同学的编号,行末换行且无空格。
输入输出样例
输入 #1
4
1 0
2 1
1 0
2
3
3
输出 #1
2 4 1
说明/提示
样例解释:
将同学2插入至同学1左边,此时队列为:
21
将同学3插入至同学2右边,此时队列为:
231
将同学4插入至同学1左边,此时队列为:
2341
将同学3从队列中移出,此时队列为:
241
同学3已经不在队列中,忽略最后一条指令
最终队列:
241
数据范围
对于20%的数据,有N≤10;
对于40%的数据,有N≤1000;
对于100%的数据,有N,M≤100000;
这题是今天的重点,采用结构体数组进行实现,用来分别记录下每个同学左右的人,每一次进入操作则是将那位同学的左右以及左边同学的右和右边同学的左进行修改,每一次退出操作则是将退出同学左边的右和右边同学的左进行修改,输出时,从1开始去寻找最左边(左为0)后依次输出到最右边(右为0)完成。
①数组的做法
#include <iostream>
#include <map>
using namespace std;
typedef struct Node
{
int left = 0;
int data = 0;
int right = 0;
} Node;
map<int, int> HaveGone;
int main()
{
int n;
cin >> n;
Node node[n + 1];
//读入部分
node[1].data = 1;
int i;
int position, mode;
int tmp;
for (i = 2; i <= n; i++)
{
cin >> position;
cin >> mode;
if (!mode)
{
node[i].data = i;
node[i].right = position;
tmp = node[position].left;
node[position].left = i;
if (tmp)
{
node[tmp].right = i;
node[i].left = tmp;
}
}
else
{
node[i].data = i;
node[i].left = position;
tmp = node[position].right;
node[position].right = i;
if (tmp)
{
node[tmp].left = i;
node[i].right = tmp;
}
}
}
cin >> n;
//出队部分
while (n--)
{
cin >> position;
if (HaveGone.count(position))
continue;
HaveGone[position] = position;
node[node[position].left].right = node[position].right;
node[node[position].right].left = node[position].left;
}
//显示部分
tmp = 1;
while (node[tmp].left != 0)
tmp = node[tmp].left; //把tmp移动到第一个
while (node[tmp].right != 0)
{
cout << node[tmp].data << ' ';
tmp = node[tmp].right;
}
}
②双向链表做法
//by EiJUn
#include <cstdio>
#include <map>
#include <string>
#include <vector>
#include <stack>
#include <queue>
#include <malloc.h>
#include <list>
#include <cstring>
using namespace std;
struct Queue
{
int num;
Queue *left = NULL;
Queue *right = NULL;
} stu[100000];
Queue *head = &stu[1];
void Insert(int i, int k, int ope)
{
Queue *tmp1, *tmp2;
tmp1 = &stu[i];
tmp2 = &stu[k];
if (ope == 1)
{
if (tmp2->right == NULL)
{
tmp1->right = NULL;
tmp1->left = tmp2;
tmp2->right = tmp1;
}
else
{
tmp1->left = tmp2;
tmp1->right = tmp2->right;
tmp2->right->left = tmp1;
tmp2->right = tmp1;
}
}
else
{
if (tmp2->left == NULL)
{
tmp1->left = NULL;
tmp1->right = tmp2;
tmp2->left = tmp1;
}
else
{
tmp1->right = tmp2;
tmp1->left = tmp2->left;
tmp2 = tmp2->left;
tmp2->right = tmp1;
tmp2 = tmp2->right->right;
tmp2->left = tmp1;
}
if(tmp2==head)
{
head=tmp2->left;
}
}
}
void Delete(int x)
{
Queue *tmp = &stu[x];
Queue *tail = head;
if (tmp != NULL)
{
if(tmp==head)
{
head=tmp->right;
}
else if(tmp->right==NULL)
{
tmp->left->right=NULL;
}
else{
tmp->left->right = tmp->right;
tmp->right->left = tmp->left;
}
}
}
int main()
{
//freopen("P1160_3.in", "r", stdin);
bool inQueue[100001];
memset(inQueue, false, sizeof(inQueue));
inQueue[1] = true;
int n;
scanf("%d", &n);
stu[1].num=1;
for (int j = 2; j <= n; j++)
{
inQueue[j] = true;
int k, p;
stu[j].num=j;
scanf("%d%d", &k, &p);
Insert(j, k, p);
}
int m;
scanf("%d", &m);
while (m--)
{
int x;
scanf("%d", &x);
if (inQueue[x] == true)
{
Delete(x);
inQueue[x] = false;
}
}
Queue *p = head;
while (p != NULL)
{
printf("%d ", p->num);
p = p->right;
}
}
③(搬运题解)STL list 的做法,list已经可以在公众号回复
用list建设链表,用erased数组表示被删除以便不输出
#include <cstdio>
#include <list>
using namespace std;
using Iter = list<int>::iterator;
const int maxN = 1e5 + 10;
Iter pos[maxN];
list<int> queList;
bool erased[maxN];
int N;
void buildQueue()
{
queList.push_front(1);
pos[1] = queList.begin();
for (int i = 2; i <= N; i++)
{
int k, p;
scanf("%d%d", &k, &p);
if (p == 0)
{
pos[i] = queList.insert(pos[k], i); //left
}
else
{
auto nextIter = next(pos[k]);
pos[i] = queList.insert(nextIter, i); //right
}
}
int M;
scanf("%d", &M);
for (int x, i = 1; i <= M; i++)
{
scanf("%d", &x);
if (!erased[x])
{
queList.erase(pos[x]);
}
erased[x] = true;
}
}
int main()
{
scanf("%d", &N);
buildQueue();
bool first = true;
for (int x: queList)
{
if (!first)
putchar(' ');
first = false;
printf("%d", x);
}
putchar('\n');
return 0;
}
题目5背景
小晨的电脑上安装了一个机器翻译软件,他经常用这个软件来翻译英语文章。
题目描述
这个翻译软件的原理很简单,它只是从头到尾,依次将每个英文单词用对应的中文含义来替换。对于每个英文单词,软件会先在内存中查找这个单词的中文含义,如果内存中有,软件就会用它进行翻译;如果内存中没有,软件就会在外存中的词典内查找,查出单词的中文含义然后翻译,并将这个单词和译义放入内存,以备后续的查找和翻译。
假设内存中有M个单元,每单元能存放一个单词和译义。每当软件将一个新单词存入内存前,如果当前内存中已存入的单词数不超过M−1,软件会将新单词存入一个未使用的内存单元;若内存中已存入M个单词,软件会清空最早进入内存的那个单词,腾出单元来,存放新单词。
假设一篇英语文章的长度为N个单词。给定这篇待译文章,翻译软件需要去外存查找多少次词典?假设在翻译开始前,内存中没有任何单词。
输入格式
共2行。每行中两个数之间用一个空格隔开。
第一行为两个正整数M,N,代表内存容量和文章的长度。
第二行为N个非负整数,按照文章的顺序,每个数(大小不超过1000)代表一个英文单词。文章中两个单词是同一个单词,当且仅当它们对应的非负整数相同。
输出格式
一个整数,为软件需要查词典的次数。
输入输出样例
输入 #1
3 7
1 2 1 5 4 4 1
输出 #1
5
说明/提示
样例解释
整个查字典过程如下:每行表示一个单词的翻译,冒号前为本次翻译后的内存状况:
1
:查找单词 1 并调入内存。1 2
:查找单词 2 并调入内存。1 2
:在内存中找到单词 1。1 2 5
:查找单词 5 并调入内存。2 5 4
:查找单词 4 并调入内存替代单词 1。2 5 4
:在内存中找到单词 4。5 4 1
:查找单词 1 并调入内存替代单词 2。
共计查了5次词典。
数据范围
对于10%的数据有M=1,N≤5;
对于100%的数据有1≤M≤1001,1≤N≤10001
解题思路:
用STL的queue来实现最先进内存的字典输出,用set来判断是否在内存中
#include <iostream>
#include <set>
#include <queue>
using namespace std;
set<int> memorize;
queue<int> mmemorize;
int main()
{
int M,N;
cin>>M>>N;
int tmp,count=0;
while(N--)
{
cin>>tmp;
if(!memorize.count(tmp))
{
count++;
memorize.insert(tmp);
mmemorize.push(tmp);
if(memorize.size()==M+1)
{
tmp=mmemorize.front();
mmemorize.pop();
memorize.erase(tmp);
}
}
}
cout<<count;
}
用队列完成曾经的约瑟夫环问题
题目描述
nnn 个人围成一圈,从第一个人开始报数,数到mmm 的人出列,再由下一个人重新从111 开始报数,数到mmm 的人再出圈,依次类推,直到所有的人都出圈,请输出依次出圈人的编号。
输入格式
输入两个整数n,mn,mn,m。
输出格式
输出一行nnn 个整数,按顺序输出每个出圈人的编号。
输入输出样例
输入 #1
10 3
输出 #1
3 6 9 2 7 1 8 5 10 4
说明/提示
1≤m,n≤100
其他方法在以前推送已经展示过,这里展示队列的方法
//by EiJUn
#include <cstdio>
#include <map>
#include <string>
#include <vector>
#include <stack>
#include <queue>
using namespace std;
queue<int> yue;
int main()
{
int n, m, i = 1;
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++)
yue.push(i);
while (!yue.empty())
{
int tmp = yue.front();
yue.pop();
if (i % m == 0)
{
i = 1;
printf("%d ", tmp);
}
else
{
yue.push(tmp);
i++;}
}
}