Description
烽火台又称烽燧,是重要的军事防御设施,一般建在险要或交通要道上。一旦有敌情发生,白天燃烧柴草,通过浓烟表达信息;夜晚燃烧干柴,以火光传递军情,在某两座城市之间有 n n 个烽火台,每个烽火台发出信号都有一定代价。为了使情报准确地传递,在连续 m m 个烽火台中至少要有一个发出信号。请计算总共最少花费多少代价,才能使敌军来袭之时,情报能在这两座城市之间准确传递。
Input
第一行:两个整数 N,M N , M 。其中N表示烽火台的个数, M M 表示在连续 m m 个烽火台中至少要有一个发出信号。接下来 N N 行,每行一个数 Wi W i ,表示第i个烽火台发出信号所需代价。
Output
一行,表示答案。
Sample Input
5 3
1
2
5
6
2
Sample Output
4
Data Constraint
对于50%的数据, M≤N≤1,000 M ≤ N ≤ 1 , 000 。 对于100%的数据, M≤N≤100,000,Wi≤100 M ≤ N ≤ 100 , 000 , W i ≤ 100 。
Solution
很明显,对于第
i
i
个烽火台有两种状态——点燃与不点燃。
因此我们设
fi,0
f
i
,
0
和
fi,1
f
i
,
1
,
fi,0
f
i
,
0
表示前
i
i
个烽火台如果第
i
i
个不点燃需要付出的最小代价,
fi,1
f
i
,
1
则表示点燃第
i
i
个的最小代价。
如果第i个烽火台不点燃,那么在
i
i
之前的个烽火台必须至少有一个要点燃的,也就是说,
fi,0=min(fi−k,1),k∈[1,m−1]
f
i
,
0
=
m
i
n
(
f
i
−
k
,
1
)
,
k
∈
[
1
,
m
−
1
]
。
如果点燃,当前最小代价就是前
m
m
个的最小值加第个的代价,即
fi,1=min(fi−k,1+ci),k∈[1,m]
f
i
,
1
=
m
i
n
(
f
i
−
k
,
1
+
c
i
)
,
k
∈
[
1
,
m
]
注意这里包括第
m
m
个。
但是有个问题。
如果每次都枚举,那么复杂度则变为
n2
n
2
,不超时才怪。
所以,这里就是单调队列的登场\(^o^)/
单调队列,简单地说,就是能快速寻找某点前的最大/最小值的玩意儿。
对于这题来讲,当然是维护使
dt1<dt2<...<dti
d
t
1
<
d
t
2
<
.
.
.
<
d
t
i
就可解决问题了。
单调队列进出队操作
进队时,将进队的元素为
e
e
,从队尾往前扫描,直到找到一个不大于
e
e
的元素
d
d
,将
e
e
放在
d
d
之后,舍弃
e
e
之后的所有元素;如果没有找到这样一个
d
d
,则将
e
e
放在队头(此时队列里只有这一个元素)。
出队时,将出队的元素为
e
e
,从队头向后扫描,直到找到一个元素
f
f
比
e
e
后进队,舍弃
f
f
之前所有的。(实际操作中,由于是按序逐个出队,所以每次只需要出队只需要比较队头)。
附:此篇为笔者人生第一篇博客,内容和排版这些细节先不必在意啦。
CODE
var n,m,i,j,head,tail,j2:longint;
a:array[1..100000] of longint;
k:array[1..100000,1..2] of longint;
f:array[1..100000,0..1] of longint;
p:boolean;
function min(x,y:longint):longint;
begin
if x<y then exit(x) else exit(y);
end;
begin
readln(n,m);
// for i:=1 to n do f[i,0]:=maxlongint;
for i:=1 to n do readln(a[i]);
f[1,1]:=a[1];
f[2,0]:=a[1];
f[2,1]:=a[2];
k[1,1]:=a[1];
k[1,2]:=1;
tail:=1;
head:=1;
for i:=3 to m do begin
f[i,1]:=a[i];
p:=true;
for j:=tail downto head do if k[j,1]<=f[i-1,1] then begin
p:=false;
k[j+1,1]:=f[i-1,1];
k[j+1,2]:=i-1;
tail:=j+1;
break;
end;
if p then begin
k[head,1]:=f[i-1,1];
k[head,2]:=i-1;
tail:=head;
end;
f[i,0]:=k[head,1];
end;
for i:=m+1 to n do begin
p:=true;
for j:=tail downto head do if k[j,1]<=f[i-1,1] then begin
p:=false;
k[j+1,1]:=f[i-1,1];
k[j+1,2]:=i-1;
tail:=j+1;
break;
end;
if p then begin
k[head,1]:=f[i-1,1];
k[head,2]:=i-1;
tail:=head;
end;
f[i,1]:=k[head,1]+a[i];
for j:=head to tail do begin
if k[head,2]=i-m then begin
for j2:=head to tail do if k[j2,2]>k[head,2] then begin
head:=j2;
break;
end;
end;
end;
f[i,0]:=k[head,1];
end;
writeln(min(f[n,1],f[n,0]));
end.