题目:烽火传递
做法:动态规划+单调队列
状态表示:
f
[
i
]
f[i]
f[i] 表示前
i
i
i 个烽火台并点燃第
i
i
i 个烽火台的最小合法代价。
状态转移:
f
[
i
]
f[i]
f[i]
=
=
=
m
i
n
{
f
[
j
]
+
w
[
i
]
,
i
−
m
⩽
j
⩽
i
−
1
}
min\{f[j]+w[i], i-m \leqslant j \leqslant i-1\}
min{f[j]+w[i],i−m⩽j⩽i−1},最后扫描队尾
m
m
m 个
f
[
i
]
f[i]
f[i] 的值(这样就可考虑到第
i
i
i 个不点燃的情况)。
代码:
#include <iostream>
#include <cstdio>
using namespace std;
const int N=200010;
int f[N],w[N],n,m,q[N];
int main(){
int hh=0,tt=0; //f[0]=0
cin>>n>>m;
for(int i=1;i<=n;i++) scanf("%d",&w[i]);
for(int i=1;i<=n;i++){
if(q[hh]<i-m) hh++; //* <> 这里因为第i个会点燃,所以i-m<=j<=i-1
f[i]=f[q[hh]]+w[i]; //*sequence
while(hh<=tt && f[q[tt]]>=f[i]) tt--; //*<>
q[++tt]=i;//*tt
}
int res=0x3f3f3f3f;
for(int i=n-m+1;i<=n;i++) //*range
res=min(res,f[i]);
cout<<res<<endl;
return 0;
}