hdu 1257 最少拦截系统(动态规划 || 构造法-贪心 || 线段树)

Posted jpphy0

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了hdu 1257 最少拦截系统(动态规划 || 构造法-贪心 || 线段树)相关的知识,希望对你有一定的参考价值。

最少拦截系统 - 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;
}

以上是关于hdu 1257 最少拦截系统(动态规划 || 构造法-贪心 || 线段树)的主要内容,如果未能解决你的问题,请参考以下文章

HDU 1257 最少拦截系统

HDU1257:最少拦截系统

HDU1257 最少拦截系统

HDU-1257 最少拦截系统 ( 贪心 )

HDU 1257 最少拦截系统

HDU - 1257 最少拦截系统(贪心) 解题