值得注意的是,本章虽然依然有很多不错的思想和题目,但并不建议初学知识点时从这里入门。并不是因为题目难,而是讲解并没有看网上其他博客来的清楚。
本章缺少的重要科技:\(Link-Cut-Tree\),主席树,后缀自动机。
- 基础数据结构:数组,链表,队列,栈,并查集,优先队列。
例题\(1\) 猜猜数据结构(\(UVa11995\))
- 直接用\(STL\)的轮子模拟。
- 坑点:小心可能会对空的轮子执行\(pop\)导致\(RE\)
#include <bits/stdc++.h>
using namespace std;
stack <int> s;
queue <int> q;
priority_queue <int> pq;
int n, x;
int read () {
int s = 0, w = 1, ch = getchar ();
while ('9' < ch || ch < '0') {
if (ch == '-') w = -1;
ch = getchar ();
}
while ('0' <= ch && ch <= '9') {
s = s * 10 + ch - '0';
ch = getchar ();
}
return s * w;
}
int main () {
// freopen ("data.in", "r", stdin);
while (scanf ("%d", &n) == 1) {
int can1 = 1, can2 = 1, can3 = 1;
for (int i = 1; i <= n; ++i) {
if (read () == 1) {
x = read ();
s.push (x);
q.push (x);
pq.push (x);
} else {
x = read ();
if (s.empty ()) {
while (i != n) {
read (), read (), ++i;
}
can1 = can2 = can3 = 0;
break;
}
can1 &= (s.top () == x); s.pop ();
can2 &= (q.front () == x); q.pop ();
can3 &= (pq.top () == x); pq.pop ();
}
}
if (can1 + can2 + can3 > 1) {
puts ("not sure");
} else if (can1 + can2 + can3 == 0) {
puts ("impossible");
} else {
if (can1) puts ("stack");
if (can2) puts ("queue");
if (can3) puts ("priority queue");
}
while (!s.empty ()) s.pop ();
while (!q.empty ()) q.pop ();
while (!pq.empty ()) pq.pop ();
}
}
例题\(2\) 一道简单题(\(UVa11991\))
- 同样还是一个\(STL\)的轮子。直接用\(vector\)保存每一个数的出现情况即可。
#include <bits/stdc++.h>
using namespace std;
const int N = 1000010;
int n, m, arr[N];
map <int, int> mp[N];
int main () {
// freopen ("data.in", "r", stdin);
ios :: sync_with_stdio (false);
while (cin >> n >> m) {
for (int i = 1; i <= n; ++i) {
cin >> arr[i]; int w = arr[i];
mp[w][++mp[w][0]] = i;
}
for (int i = 1; i <= m; ++i) {
static int k, v;
cin >> k >> v;
cout << mp[v][k] << endl;
}
for (int i = 1; i <= n; ++i) {
mp[arr[i]].clear ();
}
}
}
例题\(3\) 阿格斯(\(LA3135\))
- 每次都取走最先发生的事件,然后再把下一次发生的情况塞回去,处理\(k\)次。
#include <bits/stdc++.h>
using namespace std;
struct Node {
int Q_num, dotime, period;
bool operator < (Node rhs) const {
return dotime == rhs.dotime ? Q_num > rhs.Q_num : dotime > rhs.dotime;
}
Node (int _Q_num = 0, int _dotime = 0, int _period = 0) {
Q_num = _Q_num, dotime = _dotime, period = _period;
}
};
priority_queue <Node> q;
char opt[10]; int k, Q_num, dotime, period;
int main () {
// freopen ("data.in", "r", stdin);
while (cin >> opt && opt[0] == 'R') {
cin >> Q_num >> period;
q.push (Node (Q_num, period, period));
}
cin >> k;
while (k--) {
Node u = q.top (); q.pop ();
cout << u.Q_num << endl;
u.dotime += u.period;
q.push (u);
}
}
例题\(4\) \(K\)个最小和(\(UVA11997\))
- 这个题就比较有意思了。
- 首先每一行的序列它们的顺序是无关紧要的,所以我们可以从小到大排个序。
- 如果只有两行的话,问题就变成了合并两个单调不下降的序列,并取其前\(k\)小。
- 推广到\(N\)行就是每次把前两行合并,取其合并后的前\(K\)大,新的序列继续向下合并。
- 因为对于任意两个序列,我们可以一直选取第二个序列的最小值,则第一个序列中比第\(K\)大要大值的一定不产生贡献。
- 现在关键就在于合并了。如果暴力合并总复杂度是\(O(N^3)\)的,难以接受。更优秀的办法是先取两个序列分别的最小值合并取走(这两个数合并产生的值一定最小且在答案里!),然后再把这个最小值后继可能的次小值插入优先队列维护,就这样每次取最小的然后向后延伸。做\(K\)次复杂度就是\(O(NlogN)\),总复杂度就是\(O(N^2logN)\)。这个东西叫做多路归并。
#include <bits/stdc++.h>
using namespace std;
const int N = 750 + 5;
int k, arr1[N], arr2[N], data[N];
struct Node {
int p1, p2;
bool operator < (Node rhs) const {
return arr1[p1] + arr2[p2] > arr1[rhs.p1] + arr2[rhs.p2];
}
int get_val () {return arr1[p1] + arr2[p2];}
Node (int _p1 = 0, int _p2 = 0) {
p1 = _p1, p2 = _p2;
}
};
priority_queue <Node> q;
int main () {
// freopen ("data.in", "r", stdin);
// freopen ("data.out", "w", stdout);
while (scanf ("%d", &k) == 1) {
memset (arr1, 0, sizeof (arr1));
for (int i = 1; i <= k; ++i) {
scanf ("%d", &arr1[i]);
}
for (int i = 2; i <= k; ++i) {
for (int j = 1; j <= k; ++j) {
scanf ("%d", &arr2[j]);
}
sort (arr2 + 1, arr2 + 1 + k); //从小到大
// printf ("arr1 : "); for (int i = 1; i <= k; ++i) printf ("%d ", arr1[i]); printf ("\n");
// printf ("arr2 : "); for (int i = 1; i <= k; ++i) printf ("%d ", arr2[i]); printf ("\n");
while (!q.empty ()) q.pop ();
for (int p1 = 1; p1 <= k; ++p1) {
q.push (Node (p1, 1));
}
for (int j = 1; j <= k; ++j) {
Node u = q.top (); q.pop ();
// printf ("u = {pos1 = %d, pos2 = %d}\n", u.p1, u.p2);
data[j] = u.get_val ();
u.p2++; q.push (u);
}
swap (arr1, data);
}
for (int i = 1; i <= k; ++i) {
printf ("%d", arr1[i]); if (i != k) putchar (' ');
}
printf ("\n");
}
}