这部分内容讲了基础算法中的区间问题,贪心问题,
核心还是扫描法和数学相关的分析
区间覆盖问题
Grass
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 const int maxn = 1e4 + 10; int n, m = 0; double L, W; class A { public: double l, r; A() = default; A(double l, double r) : l(l), r(r) {} bool operator< (const A &rhs) const { return l < rhs.l || (l == rhs.l && r < rhs.r); } } a[maxn]; void init () { m = 0; } void solve () { sort(a+1, a+1+m); double to = 0.0; int i = 1; int cnt = 0; bool ok = true ; while (to < L) { cnt++; double lst = to; for (; i <= m && a[i].l <= lst; i++) chmax(to, a[i].r); if (lst == to && to < L) { ok = false ; break ; } } if (!ok) printf ("-1\n" ); else printf ("%d\n" , cnt); } int main () { freopen("input.txt" , "r" , stdin); while (~scanf("%d%lf%lf" , &n, &L, &W)) { init(); _rep(i, 1, n) { double x, r; scanf("%lf%lf" , &x, &r); if (r < W/2.0) continue ; double dx = sqrt(r*r - W*W/4.0); a[++m] = A(x-dx, x+dx); } // then solve solve(); } }
活动执行问题,优先队列
UVA1422
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 const int Max = 10000000; const int maxn = 20000; int n; class A { public: int l, r, w; A() = default; A(int l, int r, int w) : l(l), r(r), w(w) {} bool operator< (const A &rhs) const { return r > rhs.r; } } a[maxn]; inline bool cmp(const A &a, const A &b) { return a.l < b.l; } bool check(int speed) { priority_queue<A> que; int i = 1; _rep(p, 1, maxn) { while (i <= n && a[i].l == p) que.push(a[i++]); if (que.empty()) { if (i > n) break ; else continue ; } int tot = speed; while (que.size() && tot > 0) { A t = que.top(); que.pop(); if (t.r <= p && t.w > 0) return false ; if (tot >= t.w) { tot -= t.w; t.w = 0; } else { t.w -= tot; tot = 0; que.push(t); } } } return que.empty(); } void solve () { sort(a+1, a+1+n, cmp); int L = 0, R = Max; while (L < R) { int mid = (L + R) >> 1; if (check(mid)) R = mid; else L = mid + 1; } printf ("%d\n" , L); } void init () { // } int main () { freopen("input.txt" , "r" , stdin); int kase; cin >> kase; while (kase--) { init(); scanf("%d" , &n); _rep(i, 1, n) scanf("%d%d%d" , &a[i].l, &a[i].r, &a[i].w); // solve solve(); } }
约束关系的双集合思想
UVA1450
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 const int maxn = 1e4 + 10; int n; int a[maxn], b[maxn]; bool check(int val) { int sum1 = 0, sum2 = 0; int can1 = 0, can2 = 0, cantot = 0; for (int i = 1; i <= n; i++) { sum1 += a[i], sum2 += b[i]; int del1 = max(0, sum1 - val); int del2 = max(0, sum2 - val); if (del1 > can1 || del2 > can2 || del1 + del2 > cantot) return false ; if (sum1 - can1 > 0) can1++; if (sum2 - can2 > 0) can2++; if (sum1 + sum2 > cantot) cantot++; } return true ; } void solve () { int L = 0, R = 100000; while (L < R) { int mid = (L + R) >> 1; if (check(mid)) R = mid; else L = mid + 1; } printf ("%d\n" , max(0, L - 1)); } void init () { // } int main () { freopen("input.txt" , "r" , stdin); int kase; cin >> kase; while (kase--) { init(); scanf("%d" , &n); _rep(i, 1, n) scanf("%d%d" , &a[i], &b[i]); // then solve solve(); } }
田忌赛马中的贪心选择
将田忌和齐王的马分别从速度快到慢排序
思想,尽可能消耗齐王的快马
实在不行,必输的局,就尽可能消耗自己的慢马
特别注意
a [ l 1 ] = b [ l 2 ] a[l_1]=b[l_2] a [ l 1 ] = b [ l 2 ] ,此时有两种选择
( a [ l 1 ] , b [ l 2 ] ) (a[l_1], b[l_2]) ( a [ l 1 ] , b [ l 2 ] ) 两匹最快的马直接比
不赚钱
用田忌最慢的马把齐王最快的马给消耗掉
此时 − 200 -200 − 2 0 0 ,但只要田忌最快的马还在
损失的 200 200 2 0 0 块钱,可以用田忌最快的马 和没了 最快的马 的齐王比
这损失的钱一定可以赚回来
显然,贪心选择,后者更好
因为很有可能 没了最慢的马的田忌集合 ⩾ \geqslant ⩾ 没了最快的马的齐王集合
视图还原
有一类问题是给你正视图和侧视图,问最多有几个立方体
其实这类问题并不难
不妨假设高度为 h h h 的“长条”,在正视图中看到的坐标是 x x x
在侧视图中看到的坐标是 y y y ,那么一定要在 ( x , y ) (x,y) ( x , y ) 这个点
放置这跟长条
由此我们需要枚举 for ∀ h ∈ [ 1 , max ] \textbf{for} \ \forall h \in [1, \max] for ∀ h ∈ [ 1 , max ]
针对当前的高度 h h h ,找到在正视图中高度为 h h h 的长条个数 a [ h ] a[h] a [ h ]
侧视图中对应的长条个数 b [ h ] b[h] b [ h ]
然后 max ( a [ h ] , b [ h ] ) \max(a[h], b[h]) max ( a [ h ] , b [ h ] ) 就是需要放置的长条个数
h × max ( a [ h ] , b [ h ] ) h \times \max(a[h], b[h]) h × max ( a [ h ] , b [ h ] ) 就是应该放置的正方体数量
相邻交换法
一类目标优化问题,可以考虑这样的式子
b [ i + 1 ] + b [ i ] = G 1 ( ⋯ ) b[i+1]+b[i] = G_1(\cdots) b [ i + 1 ] + b [ i ] = G 1 ( ⋯ ) ,交换 i ↔ i + 1 i \leftrightarrow i+1 i ↔ i + 1
b [ i ] + b [ i + 1 ] = G 2 ( ⋯ ) b[i]+b[i+1] = G_2(\cdots) b [ i ] + b [ i + 1 ] = G 2 ( ⋯ )
要想 i i i 排在 i + 1 i+1 i + 1 前面,必须要在 c o m p a r e compare c o m p a r e 函数中
使得 G 2 ( ⋯ ) < G 1 ( ⋯ ) G_2(\cdots) < G_1(\cdots) G 2 ( ⋯ ) < G 1 ( ⋯ )
格点相关的模拟类问题
POJ3923
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 const int maxn = 100 + 10; char mat[maxn][maxn]; int n, m; vector<char> ans; bool check(int x, int y, int x2, int y2) { _rep(i, x, x2) _rep(j, y, y2) { if (isupper(mat[i][j]) && mat[i][j] != mat[x][y]) return false ; } return true ; } void solve () { _for(x, 0, n) _for(y, 0, m) { if (!isupper(mat[x][y])) continue ; char ch = mat[x][y]; //debug(ch); int x2 = x, y2 = y; while (mat[x2+1][y] == ch) x2++; while (mat[x][y2+1] == ch) y2++; if (x2 - x < 2 || y2 - y < 2) continue ; if (check(x, y, x2, y2)) ans.push_back(ch); } sort(ans.begin(), ans.end()); for (auto x : ans) printf ("%c" , x); printf ("\n" ); } void init () { memset(mat, 0, sizeof(mat)); ans.clear(); } int main () { freopen("input.txt" , "r" , stdin); while (scanf("%d%d" , &n, &m) == 2 && n) { init(); // then get data _for(i, 0, n) scanf("%s" , mat[i]); // then solve solve(); } }