例1
烽火台又称烽燧,是重要的军事防御设施,一般建在险要或交通要道上。一旦有敌情发生,白天燃烧柴草,通过浓烟表达信息;夜晚燃烧干柴,以火光传递军情,在某两座城市之间有n个烽火台,每个烽火台发出信号都有一定代价。为了使情报准确地传递,在连续m个烽火台中至少要有一个发出信号。请计算总共最少花费多少代价,才能使敌军来袭之时,情报能在这两座城市之间准确传递。
解
fi表示在第i个烽火台一定发出信号的最小代价。
①若i小于等于m时,fi=wi
②若fi大于m时,fi=min{fj}+wi{
i−m≤j≤i−1
}
那么这么时候,我们就需要维护一个单调队列,在这个队列中所有元素在原序列中的位置和权值都是单调递增或单调递减的。然后从左到右扫一遍,计算f的同时把他丢进队尾中。具体可以先看代码。
说明:q是下标,a是权值。
const
maxn=trunc(1e+5+5);
var
f,w,a,q:array[0..maxn] of longint;
i,j,k,n,m,ans,st,en:longint;
function min(a,b:longint):longint;
begin
if a>b then exit(b);
exit(a);
end;
function max(a,b:longint):longint;
begin
if a<b then exit(b);
exit(a);
end;
begin
assign(input,'beacon.in');
reset(input);
assign(output,'beacon.out');
rewrite(output);
readln(n,m);
for i:=1 to n do
read(w[i]);
st:=1;
en:=0;
for i:=1 to n do
begin
while (i-q[st]>m)and(st<=en) do inc(st);//我们看当前队头和当前i的距离是否超过m
if i<=m then
begin
f[i]:=w[i];
end
else
f[i]:=w[i]+a[st];//队头权值最小
while (st<=en) and (a[en]>=f[i]) do dec(en);
//把当前的f丢进队列中,并且进行维护
inc(en);
a[en]:=f[i];
q[en]:=i;
end;
ans:=maxlongint;
for i:=max(1,n+1-m) to n do
ans:=min(ans,f[i]);
writeln(ans);
close(input);
close(output);
end.
为什么我们能确保这是正确的呢,因为在这个序列中q是递增的,而a是递减的,(把f丢进队列时,把前面所有比他大的都删掉,这样便能保证q递增,a递减),其次我们检查队首与当前i的距离是否大于m,仔细想想体会其中的妙处。
例2
一个猴子找到了很多香蕉树,这些香蕉树都种在同一直线上,而猴子则在这排香蕉树的第一棵树上。这个猴子当然想吃尽量多的香蕉,但它又不想在地上走,而只想从一棵树跳到另一棵树上。同时猴子的体力也有限,它不能一次跳得太远或跳的次数太多。每当他跳到一棵树上,它就会把那棵树上的香蕉都吃了。那么它最多能吃多少个香蕉呢?
解:
我们仍采用与上一题类似的方法,我们设fi,j表示当前在第i棵数,且跳了j步,显然fi,j=min{fk,j-1}+wi
wi表示第i棵数上的香蕉树,且k与i的距离小于d,那么我们在要计算f的时候,把计算出来的fx,j-1,依次丢进队列中,同时计算出f即可。
const
maxn=2000+5;
var
f:array[0..maxn,0..maxn] of longint;
q,a,s,w:array[0..maxn] of longint;
d,i,n,m,k,st,en,j,ans:longint;
function max(a,b:longint):longint;
begin
if a<b then exit(b);
exit(a);
end;
begin
//assign(input,'data.in');
//reset(input);
//assign(output,'data.out');
//rewrite(output);
readln(n,d,m);
for i:=1 to n do
read(w[i],s[i]);
f[1,0]:=w[1];
q[1]:=s[1];
a[1]:=w[1];
for j:=1 to m do
begin
fillchar(q,sizeof(q),0);
fillchar(a,sizeof(a),0);
st:=1;
en:=0;
for i:=j+1 to n do
begin
while (st<=en) and (f[i-1,j-1]>=a[en]) do dec(en);//把fx,j-1丢进队列中
inc(en);
q[en]:=s[i-1];
a[en]:=f[i-1,j-1];
while (st<=en) and (s[i]-q[st]>d) do inc(st);//看队首与i的距离是否大于d
f[i,j]:=a[st]+w[i];
ans:=max(ans,f[i,j]);
end;
end;
writeln(ans);
close(input);
close(output);
end.
the end
thank you!