最少拦截系统 - http://acm.hdu.edu.cn/showproblem.php?pid=1257
分析
样例数据 【9 8 389 207 155 300 299 170 158 65(导弹9枚,与原题不同;画图时把8也看作了导弹高度,将错就错。)】
- 图中画出所有升序链(任意一条升序链上的任意两个元素都不能被同一个拦截系统拦截)
- 最长升序链的长度为k,则拦截系统不少于k个;否则,会至少存在1个拦截系统拦截了这个最长升序链中至少2个元素,不符题意。样例 k = 3,即拦截系统不少于3个。
- 可以构造出k个非升序集合(即:反链),构造方法是:每次用所有升序链的头(最小)元素构造一个非升序集合;k次构造后剩余集合为空集。例如:{8}、{389,207,155,65}、{300,299,170,158}。
- 结论:拦截系统最少要k个。参考:狄尔沃斯(Dilworth)定理 / 偏序集的分解定理
- 问题转化为:最长升序链的长度k
代码
动态规划【O(n2), 187MS】
// hdu 1257 最少拦截系统
#include<bits/stdc++.h>
using namespace std;
#define MXH 30010
int n, h, dp[MXH];
int main(){
while(cin >> n){
memset(dp, 0, sizeof dp);
for(int i = 1; i <= n; i++){
cin >> h;
dp[h]++;
for(int j = 1; j < h; j++){ // 此循环O(n),可优化为logn,优化后成线段树
if(dp[h] < dp[j]+1) dp[h] = dp[j]+1;
}
}
for(int i = 1; i < MXH; i++) if(dp[0] < dp[i]) dp[0] = dp[i];
cout << dp[0] << endl;
}
return 0;
}
反链构造法 — 又称:贪心【O(nlogn), 31MS]
- 数组a的1…k存放已有的k个导弹拦截系统各自拦截的最后一颗导弹的高度,递增排列
- 初始时刻 k = 1
- 新出现的导弹尽量用已有的导弹拦截系统拦截,且该拦截系统的最后一颗导弹的高度是尽量低
- 新出现的导弹无法用已有的拦截系统拦截,则增加一套新的拦截系统,且是在k+1的位置
- 构造过程:
—> {8} // 初始状态,a数组的值:[8], k=1
—> {8} 、{389} // 与8构成升序链,a数组的值:[8, 389], k=2
—> {8} 、{389,207} // 与8构成升序链,a数组的值:[8, 207], k=2
—> {8} 、{389,207,155} // 与8构成升序链,a数组的值:[8, 155], k=2
—> {8} 、{389,207,155} 、{300} // 与207、155构成升序链,a数组的值:[8, 155, 300], k=3
—> {8} 、{389,207,155} 、{300,299} // 与207、155构成升序链,a数组的值:[8, 155, 299], k=3
—> {8} 、{389,207,155} 、{300,299,177} // 与155构成升序链,a数组的值:[8, 155, 177], k=3
—> {8} 、{389,207,155} 、{300,299,177,158} // 与155构成升序链,a数组的值:[8, 155, 158], k=3
—> {8} 、{389,207,155, 65} 、{300,299,177,158} // 与8构成升序链,a数组的值:[8, 65, 158], k=3
// hdu 1257 最少拦截系统
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll a[100005], k, n, t;
int main() {
while (cin >> n) {
k = 1;
memset(a, 0, sizeof(a));
cin >> t;
a[1] = t;
for (ll i = 2; i <= n; ++i) {
cin >> t;
if (t > a[k]) a[++k] = t;
else *lower_bound(a + 1, a + k + 1, t) = t;
}
cout << k << endl;
}
return 0;
}
线段树【O(nlogn),15MS】
// hdu 1257 最少拦截系统
#include <bits/stdc++.h>
using namespace std;
#define MXH 30010
int n, h;
struct Node{ int l, r, mx = 0;}tr[MXH<<2];
void build(int root, int l, int r){
Node &rt = tr[root];
rt.l = l, rt.r = r, rt.mx = 0;
if(l == r) return;
int mid = (l+r)>>1;
build(root << 1, l, mid);
build(root << 1 | 1, mid+1, r);
}
int query(int root, int l, int r){
Node &rt = tr[root];
if(rt.l == l && rt.r == r) return rt.mx;
int mid = (rt.l+rt.r)>>1;
if(r <= mid) return query(root<<1, l, r);
else if(l > mid) return query(root<<1|1, l, r);
else return max(query(root<<1, l, mid), query(root<<1|1, mid+1, r));
}
void update(int root, int h, int v){
Node &rt = tr[root];
if(rt.l == rt.r) {
rt.mx = v;
return;
}
int mid = (rt.l + rt.r)>>1;
if(h <= mid) update(root << 1, h, v);
else update(root<<1|1, h, v);
rt.mx = max(tr[root<<1].mx,tr[root<<1|1].mx);
}
int main() {
while (cin >> n) {
build(1, 1, MXH);
for (int i = 1; i <= n; ++i) {
scanf("%d", &h);
update(1, h, query(1, 1, h)+1);
}
cout << query(1, 1, MXH) << endl;
}
return 0;
}