文章目录
此题较为简单,只需开一个数组即可。但是鄙人想用STL,所以就用了vector。
#include
#include
#include
#include
using namespace std;
int main()
{
int n, m;
cin >> n >> m;
vector<int> v;
for(int i = 1; i <= n; i ++)
{
int x;
cin >> x;
v.push_back(x);
}
for(int i = 1; i <= m; i ++)
{
int x;
cin >> x;
cout << v[x - 1] << endl;
}
return 0;
}
首先,肯定不能简单的开一个二维数组;因为二维数组一定是连续的内存,显然会空间超限。
对于每一次存放操作,建立一次二维映射,对于每一次查询操作,直接输出其映射的值。
可以使用STL中的Map来进行数据离散化。带上一个 loglog 的时间复杂度能接受。
在此题中,我们使用cabinet[i][j]
来表示第 i i i个柜子的第 j j j个物品存放的东西(0表示无东西存放)。
#include
#include
#include
#include
#include
using namespace std;
int main()
{
int n, q;
cin >> n >> q;
map<int, map<int, int>> cabinet; //建立二维映射
for(int i = 1; i <= q; i ++)
{
int x;
cin >> x;
if(x == 1)
{
int i, j, k;
cin >> i >> j >> k;
cabinet[i][j] = k; //建立一次映射
}
else
{
int i, j;
cin >> i >> j;
cout << cabinet[i][j] << endl;
}
}
return 0;
}
这一题可以直接在读的时候判,并不需要什么字符串,+和*没有什么可说的,重点是-和/,是n[top-1]-n[top],原因很简单,就是后缀表达式里的定义。遇见运算符,直接找到栈顶的两个元素,弹出它们,并进行运算后将运算结果压入栈中。
栈的模板题。主要是如何读取数字,因为这里的数字可能不只一位。这里可以边读边判。
#include
#include
#include
#include
using namespace std;
int main()
{
stack<int> stk;
char op;
int num = 0, x, y;
while((op = getchar()) != '@') //这里记得加括号,否则出错
{
switch(op)
{
case '.':
{
stk.push(num);
num = 0;
break; //记得加break,以下同
}
case '+':
{
x = stk.top();
stk.pop();
y = stk.top();
stk.pop();
stk.push(x + y);
break;
}
case '-':
{
x = stk.top();
stk.pop();
y = stk.top();
stk.pop();
stk.push(y - x);
break;
}
case '*':
{
x = stk.top();
stk.pop();
y = stk.top();
stk.pop();
stk.push(x * y);
break;
}
case '/':
{
x = stk.top();
stk.pop();
y = stk.top();
stk.pop();
stk.push(y / x);
break;
}
default : //到这里说明是数字
{
num = num * 10 + op - '0';
break;
}
}
}
cout << stk.top() << endl;
return 0;
}
首先我们需要模拟一个队列,将所有的元素压进队列。在进行循环(直到队列为空为止) 首先你要知道:队列只可以在head删除,那么这就要求我们只要这个人经过判断并且不会被剔除,那么就必须把他排在队尾;若这个人正好被剔除,那先输出他,再踢除。
首先初始化队列和count,每次循环看队头,如果count已经等于m了,将其删除,并令count等于1,如果不等于m,将其压入队尾,如此循环往复。
以下来自大佬绝顶我为峰的题解博客:
.p=(p+n%m-1+m)%m;很多人问这是什么,我给大家解释一下,其实所有约瑟夫杀人问题都可以套这个公式。
p相当于指针,指向下一个要被杀的人;n%m是由于n可能会比m打,为了减小运算量,对他先取余;再加上p是由于这一回要从p的位置开始数,所以+p;减去1是因为p本身也数,我们多数了一个,所以减去1;只要有减法就可能会出现负数,防止越界要再多数一圈,也就是加m;最后再对m取余,得出p——下个被杀出圈的人。
#include
#include
#include
#include
using namespace std;
int main()
{
int n, m;
cin >> n >> m;
queue<int> q;
for(int i = 1; i <= n; i ++)
q.push(i);
int count = 1; //这里是1,而不是0
while(!q.empty())
{
if(count == m) //等于m直接删除
{
cout << q.front() << " ";
q.pop();
count = 1; //这里是1,而不是0
}
q.push(q.front());
q.pop();
count ++;
}
return 0;
}
前置知识:insert(it, val)成员函数用于在链表中插入元素。it为该链表的一个迭代器,val为待插入的值,插入后val位于it所指位置的前一位。返回值为一个迭代器,表示val插入到了哪个位置。
本题关键在于插入和删除元素的操作。插入可用insert函数来做,删除可用erase函数来做;另一个难点是要存储每个元素在数据结构中的位置,这里用了一个迭代器数组来存储每一个元素的位置。值得一提的是,insert函数插入后还可以返回插入的位置;next函数可以返回下一个迭代器;还要开一个erase数组存储每个元素是否从数据结构中被删除。
#include
#include
#include
#include
using namespace std;
const int N = 1e5 + 10;
bool erase[N]; //erase[k]记录值为k的元素是否从list中被删除,全局变量初始化为false
int main()
{
int n;
cin >> n;
list<int> line; //用一个list存储所有元素
list<int>::iterator pos[N]; //pos[k]代表数值为k的元素的迭代器值
line.push_back(1); //初始化
pos[1] = line.begin();
for(int i = 2; i <= n; i ++)
{
int k, p;
cin >> k >> p;
if(p == 0)
pos[i] = line.insert(pos[k], i); //插左边
else
{
auto next_iter = next(pos[k]); //得到值为k的下一个位置,即右边
pos[i] = line.insert(next_iter, i); //插右边
}
}
int m;
cin >> m;
while(m --)
{
int x;
cin >> x;
if(!erase[x]) //没被删过
line.erase(pos[x]);
erase[x] = true; //记录下来,已删除
}
for(auto i : line)
cout << i << " ";
return 0;
}
“软件会清空最早进入内存的那个单词”这句话提醒我们要使用队列,而不能使用栈;因为队列可以删除队头,而栈不能。
整体思路也较为简单:
用队列q充当内存,可存入m个单词。输出一个数x,用exist[x]检测x是否存在于队列q中,如果存在不用管,直接下一个;如果不存在,将其加入队列中,同时ans++(ans用来记录需要查询外存的次数,初始化为0),然后检测队列元素的个数有没有超过m,如果超过,就删除队头;没有超过,则继续。如此循环往复。
#include
#include
#include
#include
using namespace std;
const int N = 1e3 + 10;
bool exist[N]; //erase[k]记录值为k的元素是否从list中被删除,全局变量初始化为false
int main()
{
int m, n;
cin >> m >> n;
int ans = 0;
queue<int> q;
while(n --)
{
int x;
cin >> x;
if(!exist[x]) //检测队列里是否存在x,如果不存在则符合条件
{
ans ++; //内存里不存在x,需要查询
q.push(x); //压入队列
exist[x] = true; //记录队列里存在x
if(q.size() > m) //内存是否超限,是则删除队头
{
exist[q.front()] = false; //这一步不要忘了,删除后,队列里就不存在它了
q.pop();
}
}
}
cout << ans << endl;
return 0;
}
感谢大佬Md_Drew的博客题解。
这道题关键在于数人头。关键词是: ∑ k i ≤ 3 × 1 0 5 \sum {k_i} \le 3 \times 10^5 ∑ki≤3×105。这意味着总人数就这么多,我们完全可以用count_nation[M]
来表示每个nation的人数,通过队列time和nation来记录每个人来的时间和所属国家。因为题目要求船到岸的时间是升序的,所以我们不用自己排序。假设现在船到了,输入参数,我们先输入时间和人数。根据时间,把不在24小时之内的人全部删掉,需要更新队列time和nation以及count_nation数组和ans,之后再添加这只船的所有人,也要更新以上四个参数。最后输出结果。
#include
#include
#include
#include
using namespace std;
const int N = 3e5 + 10, M = 1e5 + 10;
int ans = 0;
queue<int> tm, nation;
int count_nation[M];
int main()
{
int n;
cin >> n;
while(n --)
{
int t, k;
cin >> t >> k;
while(!tm.empty()) //防止刚开始队列为空时出错 !记得要先删减后增加!
{
if(tm.front() + 86400 <= t) //满足条件说明要删减
{
int j = nation.front();
count_nation[j] --; //相应的国家人数减一
if(!count_nation[j]) //如果为0,结果就减一
ans --;
tm.pop(), nation.pop(); //要弹一起弹
continue; //这个不可少
}
break; //及时结束
}
while(k --) //现在开始输入
{
int x;
cin >> x;
tm.push(t), nation.push(x); //要压一起压
if(!count_nation[x])
ans ++; //及时更新国家人数和结果
count_nation[x] ++;
}
cout << ans << endl;
}
return 0;
}
其实也完全可以将time和nation两个参数弄成一个结构体来操作,也是可以的。
这里有一道优先队列(堆)的模板题,同时有一点贪心的思想。思路是定义一个小根堆,每次取前两个数(必定是堆中最小的两个数),弹出它们,把它们的和加入到ans中,同时压入堆中,如此循环往复。
#include
#include
#include
#include
using namespace std;
const int N = 3e5 + 10, M = 1e5 + 10;
int ans = 0;
queue<int> tm, nation;
int count_nation[M];
int main()
{
int n;
cin >> n;
priority_queue< int, vector<int>, greater<int> > fruit; //书写格式要这样
while(n --)
{
int x;
cin >> x;
fruit.push(x);
}
int ans = 0;
while(fruit.size() > 1) //注意这里的条件
{
int a, b;
a = fruit.top(), fruit.pop();
b = fruit.top(), fruit.pop();
ans += a + b;
fruit.push(a + b);
}
cout << ans << endl;
return 0;
}
感谢大佬YuJieSong的指点迷津,这道题的题意都没看明白。
题意大概是:输入一个字符串,从左往右遍历每个字符,碰见右括号( )
和 ]
都算是右括号)就往左遍历寻找最近的未匹配的左括号( (
和 [
都算是左括号),如果找到了,那么把这两个左右括号都标记为已匹配;如果没找到,或者左右括号并不匹配(两者是 (
与 ]
或者 )
和 [
),就算了,继续看下一个字符。最后得到了每个位置上字符是否被标记的数组。
输出的时候,遍历每一个字符,如果当前字符未匹配,则自行将其配对并输出;如果当前字符已匹配,直接输出即可。继续看下一个字符。对了,补充个冷知识:(
与 )
ASCII码仅相差1,而 [
与 ]
相差2。
#include
#include
#include
using namespace std;
const int N = 110;
bool match[N]; //标记每个字符是否已经匹配
int main()
{
string s;
cin >> s;
for(int i = 0; s[i]; i ++)
{
if(s[i] == ')' || s[i] == ']') //发现是右括号
{
for(int j = i - 1; s[j]; j --) //开始往左遍历
{
if((s[j] == '(' || s[j] == '[') && !match[j]) //满足条件即为未匹配的最近左括号
{
if(s[j] + 1 == s[i] || s[j] + 2 == s[i]) //看两者是否匹配
match[j] = match[i] = true; //二者匹配,则都做标记
break; //不要继续找了,直接看下一个字符
}
}
}
}
for(int i = 0; s[i]; i ++)
{
if(match[i]) //已匹配,直接输出后看下一个字符
{
cout << s[i];
continue;
}
if(s[i] == '(' || s[i] == ')') //分两种括号情况,自行配对后输出
cout << "()";
else
cout << "[]";
}
return 0;
}
据大佬说,本题是模拟栈的题目。本题自己理解出现了错误:弹出栈时可能会连续弹出多个,而不是只弹出一个。比如可能会这样:(1, 2, 3, 4) -> (3, 2, 1, 4)
。所以不要被样例给误导了。
所以思路是:对于每次查询,先用两个数组 a a a 和 b b b 存储两组数据,然后进行模拟栈的循环。每次都将原始数据 a 数组的一个数(从左到右)压入栈,判断是否与目前 b 数组的值相同,如果相同,则弄一个弹出栈的循环,每次将栈顶的数与 b 数组下标 p o s pos pos 指向的数比较,如果相同,则弹出栈,b 数组下标 p o s pos pos 也加一;一直循环比较,直到两个数不同时停止。如果 a 数组的那个数与 b 数组的数不同,则继续循环。如此循环往复。
#include
#include
#include
#include
using namespace std;
const int N = 1e5 + 10;
int main()
{
int q;
cin >> q;
while(q --)
{
int n;
cin >> n;
int a[N], b[N]; //先将两行数据存起来
for(int i = 1; i <= n; i ++)
cin >> a[i];
for(int i = 1; i <= n; i ++)
cin >> b[i];
stack<int> stk;
int pos = 1;
for(int i = 1; i <= n; i ++)
{
stk.push(a[i]); //压入数组中
while(stk.top() == b[pos] && !stk.empty()) //如果当前两数相等
{
pos ++; //指向下一个
stk.pop(); //弹出
}
}
if(stk.empty()) //为空说明弹出顺序合理
cout << "Yes" << endl;
else
cout << "No" << endl;
}
return 0;
}
感谢大佬Okarin的博客题解。本题思路如下:
set能有序地维护同一类型的元素,但相同的元素只能出现一次。对于这道题来说,我们可以用set来记录下之前出现过的所有营业额。每次输入一个新的数x后,通过lower_bound操作找到set中大于等于x的第一个数。
对于输入的一个数,有以下几种情况:
0.如果这是第一个数,直接插入到set里。
1.这个数等于x,显然最小波动值为0,我们也不需要再插入一个x放到set里了。
2.这个数大于x,通过set的特性可以很轻松的找到这个数的前驱,也就是小于x的第一个数。将两个数分别减去x,对绝对值取个min就好了。此时要将x插入到set中。
#include
#include
#include
#include
using namespace std;
const int INF = 0x3f3f3f3f;
int main()
{
int n;
cin >> n;
set<int> s; //这个初始化的操作很重要,有大佬是这么说的:这个两个值是边界值。如果第二个数比第一个数小那么lower_bound就会找到第一个数的位置,那么这个位置-1必须也得有数据,不然a--会报错。
s.insert(INF);
s.insert(-INF);
int x, ans = 0;
cin >> x; //第一个数,直接插入,并且加入ans中
s.insert(x);
ans += x;
for(int i = 2; i <= n; i ++)
{
cin >> x;
set<int>::iterator pos = s.lower_bound(x); //得到大于等于x的第一个地址
if(*pos > x)
//ans += abs(* pos - x) > abs(*(pos --) - x) ? abs(*(pos --) - x) : abs(*pos - x); 错误代码
ans += min(abs(* pos - x), abs(*(pos --) - x));
s.insert(x);
//如果两者等于的话,加的是0,所以啥都不用做
}
cout << ans << endl;
return 0;
}