【ACWing】1010。ミサイルの迎撃

件名アドレス:

https://www.acwing.com/problem/content/1012/

ある国は、敵のミサイル攻撃から身を守るためにミサイル迎撃システムを開発しました。ただし、このミサイル迎撃システムには欠点があります。最初のシェルは任意の高さに達することができますが、後続の各シェルを前のシェルより高くすることはできません。ある日、レーダーが敵のミサイル攻撃を捕らえました。システムはまだ試用段階にあるため、システムは1つしかないため、すべてのミサイルを迎撃できない可能性があります。ミサイルの高度を順番に入力します(レーダーによって提供される高度データは3000030000以下です3 0 0 0 0は正の整数であり、ミサイルの数を超えない1000~1000を1 0 0 0)、このシステムが迎撃できるミサイルの最大数、およびすべてのミサイルを迎撃するために必要なそのようなミサイル迎撃システムのセットの数を計算します。

入力形式:
1行、ミサイルの高度を順番に入力します。

出力形式:
最初の行には、傍受できるミサイルの最大数を示す整数が含まれています。2行目には、すべてのミサイルを迎撃するために装備するシステムの最小数を示す整数が含まれています。

データ範囲:
レーダーによって提供される高度データは3000030000以下です3 0 0 0 0は正の整数であり、ミサイルの数を超えない1000~1000を1 0 0 0

迎撃できるミサイルの最大数は、実際には最長の非厳密な降下サブシーケンスです。実際、すべてのミサイルを迎撃するには、厳密に下降していないサブシーケンスを少なくともミサイルの高さシーケンスに分割できる数を尋ねることです。この質問は、実際には反鎖分解定理の適用です(参照https://blog.csdn.net/qq_46105170 / article / details / 108616895)、定理は次のとおりです。最小の半順序セットを持つ反チェーン分解の数は、その最長のチェーンの長さに等しくなります。この質問では、実際、厳密に降順ではないサブシーケンスの分解の最小数は、厳密に昇順の最長のサブシーケンスの長さに等しくなります。時間計算量がありますO(nlog⁡n)O(n \ log n)O nlo gn 反鎖分解アルゴリズム、一般的なアプローチは配列ffを使用することですfすべての反鎖の最後の番号を維持し(各反鎖は厳密には降順ではありません)、新しい番号xxを考え出します。xの場合ffを見つけますfの最小値がxx以上xの数、xxを使用xに置き換えます;そのような番号がない場合は、新しいアンチチェーン、つまりxxを開きますxはそれ自体が反鎖です。数学的帰納法により、fffは単調な上昇であるため、二分法で行うことができます。アルゴリズムの正しさを証明するために、上記の方法の最適解と最初の異なる演算を検討し、最適解をxxに入れます。xとそれに続く番号、および上記のソリューションのxxによって形成されるシーケンスxとそれに続く番号で形成されるシーケンスが交換されます。交換後も(反鎖の数が変更されていないため)最適解であるため、最適解を数回調整して上記の解になることができます。上記の解決策が反鎖分解の最小の分割であることを証明します。最長の非厳密に降順のサブシーケンスを見つけることは、動的計画法で行うことができます(もちろん、反鎖分解でも行うことができます。答えは、厳密に昇順のサブシーケンスの分解の最小数です)。コードは次のように表示されます。

#include <iostream>
using namespace std;

const int N = 1010;
int a[N];
int f[N];
int n;

int main() {
    
    
    while (cin >> a[n]) {
    
    
        n++;
    }

    int res = 0;
    for (int i = 0; i < n; i++) {
    
    
        f[i] = 1;
        for (int j = 0; j < i; j++)
            if (a[i] <= a[j])
                f[i] = max(f[i], 1 + f[j]);
        res = max(res, f[i]);
    }

    cout << res << endl;

    int idx = 1;
    f[0] = a[0];
    for (int i = 1; i < n; i++) {
    
    
    	// f是单调增的,用二分找到第一个大于等于a[i]的数的下标,找不到就新开一个反链
        int l = 0, r = idx - 1;
        while (l < r) {
    
    
            int m = l + (r - l >> 1);
            if (f[m] > a[i]) r = m;
            else l = m + 1;
        }

        if (f[l] >= a[i]) f[l] = a[i];
        else f[idx++] = a[i];
    }

    cout << idx << endl;

    return 0;
}

時間計算量O(n 2)O(n ^ 2)O n2nnnはミサイルの数、スペースO(n)O(n)O n

おすすめ

転載: blog.csdn.net/qq_46105170/article/details/114221798