【算法競賽入門經典—訓練指南】學習筆記(含例題代碼與思路)第三章:實用數據結構
阿新 • • 發佈:2019-04-24
其他 ont freopen data 依然 插入 else if swa urn
值得註意的是,本章雖然依然有很多不錯的思想和題目,但並不建議初學知識點時從這裏入門。並不是因為題目難,而是講解並沒有看網上其他博客來的清楚。
本章缺少的重要科技:\(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");
}
}
【算法競賽入門經典—訓練指南】學習筆記(含例題代碼與思路)第三章:實用數據結構