単調キュー最適化DP
トピック
ビーコンタワーは重要な軍事防衛施設であり、一般的に主要な交通ルートや危険な場所に建設されています。
軍事状況が発生すると、日中は濃い煙が、夜は火の光が軍事状況を伝えるために使用されます。
特定の2つの都市の間にn個のビーコンタワーがあり、各ビーコンタワーは特定の価格で信号を送信します。
情報を正確に送信するために、m個の連続するビーコンタワーの少なくとも1つが信号を送信する必要があります。
ここで、n、m、および各ビーコンタワーのコストを入力します。2つの都市間で情報を正確に送信するために必要な最小合計コストを計算してください。
入力形式
最初の行は2つの整数n、mです。具体的な意味については、タイトルの説明を参照してください。
2行目のn個の整数は、各ビーコンタワーのコストaiを表します。
出力フォーマット
出力は、最小コストを表す整数のみです。
データ範囲
1≤n、m≤2×
105、0≤ai≤1000
入力例:
5 3
1 2 5 6 2
出力例:
4
アイデア
分析アイコン:
状態は次の f[i]
ことを表します:前部1~i
座席のビーコン条件が満たされ、最初のi
ビーコンタワーの点火スキームが設定されます。
属性:すべての適格なソリューションのセットの中で最小のコスト値。
状態計算:
コレクションを分割する方法は?
m
少なくとも1つのビーコン信号を有することを要求する継続的な主題において、すなわち、連続するm
ビーコンタワーは、少なくとも1つが点火されなければならない。f[i]
表現の意味は、最初のi
座席が点火されているため、i
前部座席の前方m
に少なくとも照明付きのビーコンタワーがあります。これは、最初にすることができi-m
、まずi-m+1
、最初にi-3
、まずi-2
、最初に、最初にi -1
。
したがって、状態計算式は次のとおりです。 f [ i] = min( f[j] ) + w[i]
(i-m<=j<=i-1)
間隔の最大値は、単調なキューによって解決できます。この質問では、f[j
]セットのメンテナンスがである単調に増加するキューを定義します。行頭が取り出されるたびに、つまりm
、間隔の長さの中で最小の値を持つものf[j]
が回答を更新するために使用されます。
実際、小さな質問がありi
ます。状態を表現するときに、なぜ最初のシートを点灯として表現する必要があるのでしょうか。
問題から始めることができn
ます。すべてのビーコンタワーには、点灯するビーコンタワーが必要です。したがって、最後のn
ビーコンについても同じことが言えます。ビーコンタワーの条件を満たすf[i]
フロントとして定義した場合1~i
、最初のi
ビーコンタワーが収集プログラムを点灯しました。次に、答えはf[n-m+1],f[n-m+2],,,,,,f[n]
間になければなりません。つまり、最初のi
席を点灯として表現することで、答えを簡単に表現することができます。これは私たちにインスピレーションを与えます。状態表現を定義するとき、私たちが定義する状態が答えを含むことができるかどうかを考慮しなければなりません。
コード
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
const int N=2e5+10;
int w[N];
int f[N];
int q[N];
int main()
{
int n,m;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) scanf("%d",&w[i]);
f[0]=0; //0座的代价为0
int tt=0,hh=0;
for(int i=1;i<=n;i++)
{
if(q[hh]<i-m) hh++; //超出滑动窗口,踢出队列
f[i]=f[q[hh]]+w[i]; //更新答案
while(hh<=tt&&f[q[tt]]>=f[i]) tt--;//维护单调递增队列 在一段区间内
//找出最小的f[j](i-m<=j<=i-1)
q[++tt]=i;
}
int res=1e9;
for(int i=n-m+1;i<=n;i++) res=min(res,f[i]); //答案在最后一段区间选
printf("%d\n",res);
return 0;
}