8.13信息学集训_数据结构
预习笔记
链表、栈、队列
B3631 单向链表
实现一个数据结构,维护一张表(最初只有一个元素 \(1\))。需要支持下面的操作,其中 \(x\) 和 \(y\) 都是 \(1\) 到 \(10^6\) 范围内的正整数,且保证任何时间表中所有数字均不相同,操作数量不多于 \(10^5\):
1 x y
:将元素 \(y\) 插入到 \(x\) 后面;2 x
:询问 \(x\) 后面的元素是什么。如果 \(x\) 是最后一个元素,则输出 \(0\);3 x
:从表中删除元素 \(x\) 后面的那个元素,不改变其他元素的先后顺序。
输入格式:第一行一个整数 \(q\) 表示操作次数;接下来 \(q\) 行,每行表示一次操作,操作具体间题目描述。
输出格式:对于每个操作 2,输出一个数字,用换行隔开。
输入样例 | 输出样例 |
---|---|
6 1 1 99 1 99 50 1 99 75 2 99 3 75 2 1 |
75 99 |
【分析】模拟一个链表,但是需要支持 \(O(1)\) 的查询,因为 \(x,y\in [1,10^6]\),所以使用 \(h[x]=idx\) 来记录 \(x\) 是那个节点。
单向链表的实现
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e6 + 10, INF = 0x3f3f3f3f;
struct Node {
int r, v;
} e[N];
int h[N], idx;
// h[x] 表示 x 所在节点下标
void insert(int x, int y) {
h[y] = ++idx;
e[h[y]] = {e[h[x]].r, y};
e[h[x]].r = h[y];
}
int find(int x) {
if (e[h[x]].r == 0) return 0;
return e[e[h[x]].r].v;
}
void del(int x) {
if (e[h[x]].r == 0) return;
e[h[x]].r = e[e[h[x]].r].r;
}
int main() {
int q, op, x, y; cin >> q, insert(0, 1);
while (q--) {
cin >> op >> x;
switch (op) {
case 1: cin >> y, insert(x, y); break;
case 2: cout << find(x) << endl; break;
case 3: del(x); break;
}
}
return 0;
}
以下是双向链表的实现
struct Node {
int l, r, v;
} e[N];
int h[N], idx;
// h[x] 表示 x 所在节点下标
void insert(int x, int y) {
h[y] = ++idx;
e[h[y]] = {h[x], e[h[x]].r, y};
e[e[h[x]].r].l = e[h[x]].r = h[y];
}
int find(int x) {
if (e[h[x]].r == 0) return 0;
return e[e[h[x]].r].v;
}
void del(int x) {
e[e[h[x]].r].l = h[x];
e[h[x]].r = e[e[h[x]].r].r;
}
P1981 [NOIP2013 普及组] 表达式求值
给定一个只包含加法和乘法的算术表达式,请你编程计算表达式的值。
输入一行需要计算的表达式,表达式中只包含数字、加法运算符‘+’和乘法运算符‘×’,且没有括号,所有参与运算的数字均为 0 到 pow(2,31)-1之间的整数。输入数据保证这一行只有 0-9、+、× 这12种字符。
输出一个整数,表示这个表达式的值,当答案长度多于4位时,请只输出最后4位,前导0不输出。
输入样例:1+1*3+4
输出样例:8
【分析】模拟/栈
先观察输入数据的格式,一定是:a+b+b+b...
那么可以考虑先读入 a
,后面死循环读入 +b
,这样就可以轻松完成数据分割。
由于有 *+
,可以将 *
先处理了,后面对于 +
直接累加即可。
构建一个数字栈,遍历字符串,数字直接入栈;如果是运算符,将当前栈顶元素 a
与 b
进行计算,同时pop,计算的结果push。
最后累加栈内全部元素即可。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
stack<long long> sta;
int a,b,ans=0, mod=10000; char ch;
int main() {
cin>>a; a %= mod; sta.push(a);
while(cin>>ch>>b) {
b %= mod;
if(ch=='*') {
a = sta.top(); sta.pop();
sta.push(a*b%mod);
} else if(ch=='+') { sta.push(b); }
}
while(!sta.empty()) {
ans += sta.top(), sta.pop();
ans %= mod;
}
cout<<ans%mod; return 0;
}
P1449 后缀表达式
所谓后缀表达式是指这样的一个表达式:式中不再引用括号,运算符号放在两个运算对象之后,所有计算按运算符号出现的顺序,严格地由左而右新进行(不用考虑运算符的优先级)。
如:3*(5–2)+7
对应的后缀表达式为:3.5.2.-*7.+@。
'@'为表达式的结束符号,'.'为操作数的结束符号。字符串长度在1000内。
输入格式:后缀表达式
输出格式:表达式的值
输入样例:3.5.2.-*7.+@
输出样例:16
【分析】模拟/栈
先观察输入数据的格式,一定是:a.a.+@
每个数字后面有一个 .
,可以据此进行数据分割。
构建一个数字栈,遍历字符串,数字直接入栈;
如果是运算符,将顺序取出两个栈顶元素 a,b
进行计算,计算的结果push。
最后栈顶元素就是答案。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
stack<int> sta; int a,b,c; char ch;
int main(){
while((ch=getchar())!='@'){
if(ch<='9' && ch>='0'){
a=a*10+ch-'0';
}else if(ch=='.'){
sta.push(a); a=0;
}else {
a=sta.top(),sta.pop();
b=sta.top(),sta.pop();
if(ch=='+'){ c = b+a; }
else if(ch=='-'){ c = b-a; }
else if(ch=='*'){ c = b*a; }
else if(ch=='/'){ c = b/a; }
sta.push(c); a=b=0;
}
}
cout<<c<<endl; return 0;
}
P5788 【模板】单调栈
给出项数为 \(n\) 的整数数列 \(a_{1 \dots n}\)。
定义函数 \(f(i)\) 代表数列中第 \(i\) 个元素之后第一个大于 \(a_i\) 的元素的下标,即 \(f(i)=\min_{i<j\leq n, a_j > a_i} \{j\}\)。若不存在,则 \(f(i)=0\)。
试求出 \(f(1\dots n)\)。
输入格式:第一行一个正整数 \(n\)。第二行 \(n\) 个正整数 \(a_{1\dots n}\)。
输出格式:一行 \(n\) 个整数 \(f(1\dots n)\) 的值。
输入样例 | 输出样例 |
---|---|
5 1 4 2 3 5 |
2 5 4 5 0 |
【数据范围】对于 \(100\%\) 的数据,\(1 \le n\leq 3\times 10^6\),\(1\leq a_i\leq 10^9\)。
【分析】
- 求每个元素右边第一个大于该元素的值的下标。
- 维护一个单调栈,可以画一下下面这个图。
上图是从左向右遍历的单调递减栈,也可以维护一个从右向左遍历的单调递增栈。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 3e6 + 10, INF = 0x3f3f3f3f;
int n, a[N], st[N], hh, ans[N];
int main() {
cin >> n;
for (int i = 1; i <= n; i++)
cin >> a[i];
// 从左向右 : 维护一个单减栈
for (int i = 1; i <= n; i++) {
while (hh && a[st[hh]] < a[i]) {
ans[st[hh]] = i;
hh--;
}
st[++hh] = i;
}
// 从右向左 : 维护一个单增栈
/*hh = 0;
for (int i = n; i >= 1; i--) {
while (hh && a[st[hh]] <= a[i])
hh--;
ans[i] = st[hh];
st[++hh] = i;
}*/
for (int i = 1; i <= n; i++)
cout << ans[i] << " \n"[i == n];
return 0;
}
P1886 滑动窗口 /【模板】单调队列
有一个长为 \(n\) 的序列 \(a\),以及一个大小为 \(k\) 的窗口。现在这个从左边开始向右滑动,每次滑动一个单位,求出每次滑动后窗口中的最大值和最小值。
例如:The array is \([1,3,-1,-3,5,3,6,7]\), and \(k = 3\)。
输入格式: 输入一共有两行,第一行有两个正整数 \(n,k\)。第二行 \(n\) 个整数,表示序列 \(a\)
输出格式:输出共两行,第一行为每次窗口滑动的最小值;第二行为每次窗口滑动的最大值
输入样例 | 输出样例 |
---|---|
8 3 1 3 -1 -3 5 3 6 7 |
-1 -3 -3 -3 3 3 3 3 5 5 6 7 |
【数据范围】\(1\le k \le n \le 10^6\),\(a_i \in [-2^{31},2^{31})\)。
【分析】考虑两个问题
- 如何确定窗口大小:可以通过下标求差完成
- 如何确定窗口内的最值:可以利用单调队列完成
这里为了便于理解,我们画一个样例图
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e6 + 10, INF = 0x3f3f3f3f;
int n, m, a[N], q[N], ans[N];
int main() {
cin >> n >> m;
for (int i = 1; i <= n; i++)
cin >> a[i];
// 最小值 - 维护一个单调递增队列
int hh = 0, tt = -1;
for (int i = 1; i <= n; i++) {
while (hh <= tt && a[q[tt]] >= a[i]) tt--;
while (hh <= tt && i - q[hh] + 1 > m) hh++;
q[++tt] = i;
if (i >= m)
cout << a[q[hh]] << " \n"[i == n];
}
// 最大值 - 维护一个单调递减队列
hh = 0, tt = -1;
for (int i = 1; i <= n; i++) {
while (hh <= tt && a[q[tt]] <= a[i]) tt--;
while (hh <= tt && i - q[hh] + 1 > m) hh++;
q[++tt] = i;
if (i >= m)
cout << a[q[hh]] << " \n"[i == n];
}
return 0;
}
P1901 发射站
某地有 \(N\) 个能量发射站排成一行,每个发射站 \(i\) 都有不相同的高度 \(H_i\),并能向两边(两端的发射站只能向一边)同时发射能量值为 \(V_i\) 的能量,发出的能量只被两边最近的且比它高的发射站接收。显然,每个发射站发来的能量有可能被 \(0\) 或 \(1\) 或 \(2\) 个其他发射站所接受。
请计算出接收最多能量的发射站接收的能量是多少。
输入格式:第 \(1\) 行一个整数 \(N\)。第 \(2\) 到 \(N+1\) 行,第 \(i+1\) 行有两个整数 \(H_i\) 和 \(V_i\),表示第 \(i\) 个人发射站的高度和发射的能量值。
输出格式:输出仅一行,表示接收最多能量的发射站接收到的能量值。答案不超过 32 位带符号整数的表示范围。
输入样例 | 输出样例 |
---|---|
3 4 2 3 5 6 10 |
7 |
【数据范围】\(1\le N\le 10^6,1\le H_i\le 2\times 10^9,1\le V_i\le 10^4\)。
【分析】
- 法1:直接暴力模拟,左右查找合适的值,可以过 40%,会 TLE。
- 法2:维护单调栈(单调递减栈:栈底到栈顶元素单调递减)
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N=1e6+10,INF=0x3f3f3f3f;
int n,h[N],v[N],st[N],head=0;
LL ans[N];
void slove1() { // 预计 TLE
for(int i=1; i<=n; i++) {
int l=i-1, r=i+1;
while(l>0 && h[l]<=h[i]) l--;
while(r<=n && h[r]<=h[i]) r++;
ans[l]+=v[i], ans[r]+=v[i];
}
}
void slove2() { // 维护一个单调栈
for(int i=1; i<=n; i++) {
while(head && h[st[head]] < h[i]) {
ans[i] += v[st[head]], head--;
}
ans[st[head]] += v[i];
st[++head] = i;
}
}
int main() {
scanf("%d",&n);
for(int i=1; i<=n; i++) scanf("%d%d",&h[i],&v[i]);
slove1();
for(int i=1; i<=n; i++) ans[1]=max(ans[1],ans[i]);
printf("%lld\n",ans[1]);
return 0;
}
P1540 [NOIP2010 提高组] 机器翻译
翻译软件从头到尾,依次将每个英文单词用对应的中文含义来替换。对于每个英文单词,软件会先在内存中查找这个单词的中文含义,如果内存中有,软件就会用它进行翻译;如果内存中没有,软件就会在外存中的词典内查找,查出单词的中文含义然后翻译,并将这个单词和译义放入内存,以备后续的查找和翻译。
假设内存中有 \(M\) 个单元,每单元能存放一个单词和译义。每当软件将一个新单词存入内存前,如果当前内存中已存入的单词数不超过 \(M-1\),软件会将新单词存入一个未使用的内存单元;若内存中已存入 \(M\) 个单词,软件会清空最早进入内存的那个单词,腾出单元来,存放新单词。
假设一篇英语文章的长度为 \(N\) 个单词。给定这篇待译文章,翻译软件需要去外存查找多少次词典?假设在翻译开始前,内存中没有任何单词。
输入格式:共 \(2\) 行。每行中两个数之间用一个空格隔开。
第一行为两个正整数 \(M,N\),代表内存容量和文章的长度。
第二行为 \(N\) 个非负整数,按照文章的顺序,每个数(大小不超过 \(1000\))代表一个英文单词。文章中两个单词是同一个单词,当且仅当它们对应的非负整数相同。
输出格式:一个整数,为软件需要查词典的次数。
输入样例 | 输出样例 |
---|---|
3 7 1 2 1 5 4 4 1 |
5 |
【数据范围】\(1 \leq M \leq 100\),\(1 \leq N \leq 1000\)。
数组模拟队列
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e6 + 10, INF = 0x3f3f3f3f;
int n, m, a[N], st[N], q[N], hh, tt = -1, ans;
int main() {
cin >> m >> n;
for (int i = 1, x; i <= n; i++) {
cin >> x, a[i] = x;
if (!st[x]) {
while (tt - hh + 1 >= m) st[q[hh++]] = 0;
q[++tt] = x, ans++, st[x] = 1;
}
}
cout << ans;
return 0;
}
使用STL
点击查看代码
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 10;
int m, n, v, ans; bool st[N];
queue<int> q;
int main() {
scanf("%d%d", &m, &n);
for (int i = 1; i <= n; i++) {
scanf("%d", &v);
if (st[v]) continue;
q.push(v), ans++, st[v] = 1;
while (q.size() > m)
st[q.front()] = 0, q.pop();
}
printf("%d\n", ans);
return 0;
}
优先队列、堆
P2085 最小函数值
有 \(n\) 个函数,分别为 \(F_1,F_2,\dots,F_n\)。定义 \(F_i(x)=A_ix^2+B_ix+C_i(x\in\mathbb N*)\)。给定这些 \(A_i\)、\(B_i\) 和 \(C_i\),请求出所有函数的所有函数值中最小的 \(m\) 个(如有重复的要输出多个)。
输入格式:第一行输入两个正整数 \(n\) 和 \(m\),以下 \(n\) 行每行三个正整数,其中第 \(i\) 行的三个数分别为 \(A_i\)、\(B_i\) 和 \(C_i\)。
输出格式:输出将这 \(n\) 个函数所有可以生成的函数值排序后的前 \(m\) 个元素。这 \(m\) 个数应该输出到一行,用空格隔开。
输入样例 | 输出样例 |
---|---|
3 10 4 5 3 3 4 5 1 7 1 |
9 12 12 19 25 29 31 44 45 54 |
【数据范围】 \(1 \leq n,m\le10000\),\(1 \leq A_i\le10,B_i\le100,C_i\le10^4\)。
点击查看代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e6 + 10, INF = 0x3f3f3f3f;
int n, m, a[N], b[N], c[N];
namespace BF {
void solve() {
priority_queue<int, vector<int>, greater<int>> q; // down
for (int x = 1; x <= m; x++)
for (int i = 1; i <= n; i++) {
int t = a[i] * x * x + b[i] * x + c[i];
q.push(t);
}
while (m--) {
cout << q.top() << " ";
q.pop();
}
}
}; // namespace BF
namespace BF2 {
struct T {
int a, b, c, x;
int cal() const { return a * x * x + b * x + c; }
bool operator<(const T& t) const { return cal() > t.cal(); }
};
void solve() {
priority_queue<T> q;
for (int i = 1; i <= n; i++)
q.push({a[i], b[i], c[i], 1});
while (m--) {
T t = q.top();
cout << t.cal() << " ";
t.x++, q.push(t), q.pop();
}
}
}; // namespace BF2
int main() {
cin >> n >> m;
for (int i = 1; i <= n; i++)
cin >> a[i] >> b[i] >> c[i];
BF2::solve();
return 0;
}
P1090 [NOIP2004 提高组] 合并果子 / [USACO06NOV] Fence Repair G
\(n\) 堆果子经过 \(n-1\) 次合并后, 剩下一堆,把两堆果子合并到一起,消耗的体力等于两堆果子的重量之和。求最小消耗体力。
输入格式:第一行是一个整数 n ,表示果子的种类数。
第二行包含 n个整数,第 i 个整数 \(a_i\) 是第 i 种果子的数目。
输出格式:最小的体力耗费值。输入数据保证这个值小于 \(2^{31}\)。
说明/提示:\(1 ≤ n ≤ 10000, 1 ≤ a_i ≤ 20000\)。
【分析】贪心策略:每次选择最小的两个元素进行合并。
P6033 [NOIP2004 提高组] 合并果子 加强版
【数据规模与约定】
- Subtask 1(10 points):\(1 \leq n \leq 8\)。
- Subtask 2(20 points):\(1 \leq n \leq 10^3\)。
- Subtask 3(30 points):\(1 \leq n \leq 10^5\)。
- Subtask 4(40 points):\(1 \leq n \leq 10^7\)。
对于全部的测试点,保证 \(1 \leq a_i \leq 10^5\)。
【分析】\(10^7\),使用堆排序 \(O(nlog)\) 无法通过,考虑 \(O(n)\)
切入点:\(1 \leq a_i \leq 10^5\) ,单个元素大小较小,可以使用计数排序进行第一次处理,但是后续的还需要插入 \(n-1\) 怎么排序呢?
-
思路1:整体有序,插入一个新生成的数据,可以考虑插入排序优化,但是还是会TLE。
-
思路2:原数据排序后升序,新生成的数据一定是递增的,考虑分别维护单调递增队列1、2,每次选择队首最小的2个元素,进行合并,之后在插入队列2。复杂度分析:计数排序 \(O(n)\),维护两个队列 \(O(n)\),整体复杂度 \(O(n)\)。
点击查看代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e7 + 5, INF = 0x3f3f3f3f;
int n, a[N];
namespace IO {
ll read() {
ll v = 0, f = 1; char c = getchar();
while (c < '0' || c > '9') {
if (c == '-') f = -1;
c = getchar();
}
while (c >= '0' && c <= '9')
v = v * 10 + c - '0', c = getchar();
return v * f;
}
}; // namespace IO
namespace Heap {
void solve() {
priority_queue<int, vector<int>, greater<int>> q;
for (int i = 1; i <= n; i++) q.push(a[i]);
int ans = 0, a, b;
while (q.size() > 1) {
a = q.top(), q.pop();
b = q.top(), q.pop();
q.push(a + b), ans += a + b;
}
cout << ans << endl;
}
}; // namespace Heap
namespace Queue {
void solve() {
vector<int> st(1e5 + 5, 0);
queue<ll> q1, q2;
for (int i = 1; i <= n; i++) st[a[i]]++;
for (int i = 1; i <= 1e5; i++)
while (st[i]--) q1.push(i);
ll ans = 0, x, y;
for (int i = 1; i < n; i++) {
if (q2.empty() || q1.size() && q1.front() < q2.front())
x = q1.front(), q1.pop();
else x = q2.front(), q2.pop();
if (q2.empty() || q1.size() && q1.front() < q2.front())
y = q1.front(), q1.pop();
else y = q2.front(), q2.pop();
ans += x + y, q2.push(x + y);
}
cout << ans << endl;
}
}; // namespace Queue
int main() {
// using namespace IO;
// n = read();
// for (int i = 1; i <= n; i++)
// a[i] = read();
scanf("%d", &n);
for (int i = 1; i <= n; i++)
scanf("%d", &a[i]);
// Heap::solve();
Queue::solve();
return 0;
}
P1631 序列合并
有两个长度为 \(N\) 的单调不降序列 \(A,B\),在 \(A,B\) 中各取一个数相加可以得到 \(N^2\) 个和,求这 \(N^2\) 个和中最小的 \(N\) 个。
输入格式:第一行一个正整数 \(N\),第二行 \(N\) 个整数 \(A_{1\dots N}\),第三行 \(N\) 个整数 \(B_{1\dots N}\)。
输出格式:一行 \(N\) 个整数,从小到大表示这 \(N\) 个最小的和。
输入样例 | 输出样例 |
---|---|
3 2 6 6 1 4 8 |
3 6 7 |
【数据范围】\(1 \le N \le 10^5\),\(1 \le a_i,b_i \le 10^9\)。
【分析】看图说话,哪里存在答案?
点击查看代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 3e6 + 10, INF = 0x3f3f3f3f;
int n, a[N], b[N];
priority_queue<int, vector<int>, greater<int>> q;
int main() {
cin >> n;
for (int i = 1; i <= n; i++) cin >> a[i];
for (int i = 1; i <= n; i++) cin >> b[i];
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n / i; j++)
q.push(a[i] + b[j]);
for (int i = 1; i <= n; i++)
cout << q.top() << " \n"[i == n], q.pop();
return 0;
}
P2251 质量检测
为了检测生产流水线上总共 \(N\) 件产品的质量,我们首先给每一件产品打一个分数 \(A\) 表示其品质,然后统计前 \(M\) 件产品中质量最差的产品的分值 \(Q[m] = min\{A_1, A_2, ... A_m\}\),以及第 2 至第 \(M + 1\) 件的 $Q[m + 1], Q[m + 2] $... 最后统计第 \(N - M + 1\) 至第 \(N\) 件的 \(Q[n]\)。根据 \(Q\) 再做进一步评估。
请你尽快求出 \(Q\) 序列。
输入格式:输入共两行,第一行共两个数 \(N\)、\(M\),由空格隔开,含义如前述;第二行共 \(N\) 个数,表示 \(N\) 件产品的质量。
输出格式:输出共 \(N - M + 1\) 行,第 1 至 \(N - M + 1\) 行每行一个数,第 \(i\) 行的数 \(Q[i + M - 1]\)。含义如前述。
输入样例 | 输出样例 |
---|---|
10 4 16 5 6 9 5 13 14 20 8 12 |
5 5 5 5 5 8 8 |
【数据范围】\(M \le N, A \le 1 000 000\)
【分析】滑动窗口裸题
点击查看代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 3e6 + 10, INF = 0x3f3f3f3f;
int n, m, a[N], q[N];
int main() {
cin >> n >> m;
for (int i = 1; i <= n; i++) cin >> a[i];
int hh = 0, tt = -1;
for (int i = 1; i <= n; i++) {
while (hh <= tt && a[q[tt]] >= a[i]) tt--;
while (hh <= tt && i - q[hh] + 1 > m) hh++;
q[++tt] = i;
if (i >= m) cout << a[q[hh]] << endl;
}
return 0;
}
课后练习
P1739 表达式括号匹配
【数据范围】
【分析】
点击查看代码
P1996 约瑟夫问题
n 个人围成一圈,从第一个人开始报数,数到 m 的人出列,再由下一个人重新从 1 开始报数,数到 m 的人再出圈,依次类推,直到所有的人都出圈,请输出依次出圈人的编号。
输入格式:输入两个整数 n,m(1≤m,n≤100)。
输出格式:输出一行 n 个整数,按顺序输出每个出圈人的编号。
输入样例:10 3
输出样例:3 6 9 2 7 1 8 5 10 4
使用STL
点击查看代码
#include<bits/stdc++.h>
using namespace std;
queue<int> que;
int main(){
int n,m,cnt=1; cin>>n>>m;
for(int i=1; i<=n; i++) que.push(i);
while(!que.empty()){
if(cnt<m){
que.push(que.front());
que.pop(); cnt++;
}else if(cnt==m){
cout<<que.front()<<" ";
que.pop(); cnt=1;
}
} return 0;
}
P1808 单词分类
【题目描述】
两个单词可以分为一类当且仅当组成这两个单词的各个字母的数量均相等。
例如 "AABAC",它和 "CBAAA" 就可以归为一类,而和 "AAABB" 就不是一类。
现在Oliver有N个单词,所有单词均由大写字母组成,每个单词的长度不超过100。
你要告诉Oliver这些单词会被分成几类。
【输入格式】输入文件的第一行为单词个数N,以下N行每行为一个单词。
【输出格式】输出文件仅包含一个数,表示这N个单词分成的类数
输入样例 | 输出样例 |
---|---|
3 AABAC CBAAA AAABB |
2 |
点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=1e4+1;
char s[N][101];
int main(){
int n; cin>>n;
for(int i=1; i<=n; i++){
scanf("%s", s[i]);
sort(s[i], s[i]+strlen(s[i]));
}
map<string,int> m;
for(int i=1; i<=n; i++){
m.insert(pair<string,int>(s[i],1));
}
printf("%d", m.size());
return 0;
}
B3616 【模板】队列
请你实现一个队列(queue),支持如下操作:
push(x)
:向队列中加入一个数 \(x\)。pop()
:将队首弹出。如果此时队列为空,则不进行弹出操作,并输出ERR_CANNOT_POP
。query()
:输出队首元素。如果此时队列为空,则输出ERR_CANNOT_QUERY
。size()
:输出此时队列内元素个数。
输入格式:第一行,一个整数 \(n\),表示操作的次数。
接下来 \(n\) 行,每行表示一个操作。格式如下:
1 x
,表示将元素x
加入队列。2
,表示将队首弹出队列。3
,表示查询队首。4
,表示查询队列内元素个数。
输出格式:输出若干行,对于每个操作,按「题目描述」输出结果。每条输出之间应当用空行隔开。
#include <bits/stdc++.h>
using namespace std;
const int N = 1e4 + 10;
int que[N], head = 0, tol = 0;
void push(int x) {
que[tol++] = x;
}
void pop() {
if (head == tol) cout << "ERR_CANNOT_POP" << endl;
else ++head;
}
void query() {
if (head >= tol) cout << "ERR_CANNOT_QUERY" << endl;
else cout << que[head] << endl;
}
int size() {
return tol - head;
}
int main() {
int n, op, x; cin >> n;
while (n--) {
cin >> op;
switch (op) {
case 1: cin >> x, push(x); break;
case 2: pop(); break;
case 3: query(); break;
case 4: cout << size() << endl; break;
}
}
return 0;
}
P3378 【模板】堆
给定一个数列,初始为空,请支持下面三种操作:
- 给定一个整数 x,请将 x 加入到数列中。
- 输出数列中最小的数。
- 删除数列中最小的数(如果有多个数最小,只删除 1 个)。
输入格式:第一行是一个整数,表示操作的次数 n。
接下来 n 行,每行表示一次操作。每行首先有一个整数 op 表示操作类型。
若 op=1,则后面有一个整数 x,表示要将 x 加入数列。
若 op=2,则表示要求输出数列中的最小数。
若 op=3,则表示删除数列中的最小数。如果有多个数最小,只删除 1 个。
输出格式:对于每个操作 2,输出一行一个整数表示答案。
输入样例 | 输出样例 |
---|---|
5 1 2 1 5 2 3 2 |
2 5 |
【分析】
点击查看代码
#include<iostream>
#include<queue>
using namespace std;
priority_queue<int, vector<int>, greater<int> > pq;
//priority_queue<int, vector<int>, less<int> > pq;
int main(){
int n; cin>>n;
for(int i=1; i<=n; i++){
int f; cin>>f;
if(f==1){
int x; cin>>x; pq.push(x);
}else if(f==2){
cout<<pq.top()<<endl;
}else if(f==3){
pq.pop();
}
} return 0;
}
P4715 【深基16.例1】淘汰赛
有 2^n(n≤7) 个国家参加世界杯决赛圈且进入淘汰赛环节。
我经知道各个国家的能力值,且都不相等。能力值高的国家和能力值低的国家踢比赛时高者获胜。
1 号国家和 2 号国家踢一场比赛,胜者晋级。3 号国家和 4 号国家也踢一场,胜者晋级……晋级后的国家用相同的方法继续完成赛程,直到决出冠军。
给出各个国家的能力值,请问亚军是哪个国家?
输入样例 | 输出样例 |
---|---|
3 4 2 3 1 10 5 9 7 |
1 |
点击查看代码
#include<iostream>
#include<cmath>
#include<queue>
#include<map>
using namespace std;
int main() {
int n; cin>>n; n = pow(2, n); // n=1<<n;
queue<pair<int, int> > que;
for(int i=1; i<=n; i++) {
int a; cin>>a; que.push(make_pair(i, a));
}
while( que.size() > 2 ){
pair<int, int> x,y;
x = que.front(); que.pop();
y = que.front(); que.pop();
if(x.second > y.second) que.push(x);
else que.push(y);
}
pair<int, int> x,y;
x = que.front(); que.pop();
y = que.front(); que.pop();
if(x.second > y.second) cout<<y.first;
else cout<<x.first;
return 0;
}