習題:烽火傳遞(DP+單調隊列)


                                                                           烽火傳遞
【題目描述】
烽火台又稱烽燧,是重要的防御設施,一般建在險要處或交通要道上。一旦有敵情發生,白天燃燒柴草,通過濃煙表達信息:夜晚燃燒干柴,以火光傳遞軍情。在某兩座城市之間有n個烽火台,每個烽火台發出信號都有一定的代價。為了使情報准確的傳遞,在m個烽火台中至少要有一個發出信號。現輸入n、m和每個烽火台發出的信號的代價,請計算總共最少需要花費多少代價,才能使敵軍來襲之時,情報能在這兩座城市之間准確的傳遞!!!
【輸入描述】
第一行有兩個數n,m(1<=n,m<=1000000)分別表示n個烽火台,在m個烽火台中至少要有一個發出信號。
第二行為n個數,表示每一個烽火台的代價。
【輸出描述】
一個數,即最小代價。
【樣例輸入】
5 3
1 2 5 6 2
【樣例輸出】
4

分析:

多增加兩個點表示兩座城市,將它們看做代價為0的烽火台,然后很容易得到這個式子:

f[i]:=min(f[j])+a[i](i-m<=j<i)

然后用單調隊列優化,隊列元素保存f數組的值,維護單調遞增隊列,每次取隊頭即可。

代碼1(DP+單調隊列):

program fire;
var
  f,a,b,g:array[0..1000001]of longint;
  n,i,m,h,t:longint;
procedure work(x:longint);
begin
  t:=t+1; b[t]:=f[x]; g[t]:=x;
  while (b[t]<=b[t-1])and(t>h) do
    begin
      t:=t-1; b[t]:=b[t+1]; g[t]:=g[t+1];
    end;
  if x-g[h]=m then h:=h+1;
end;
begin
  assign(input,'fire.in');
reset(input);
assign(output,'fire.out');
rewrite(output);
  readln(n,m);
  for i:=1 to n do
   read(a[i]);
  n:=n+1;
  f[0]:=0; b[1]:=0;g[1]:=0;h:=1; t:=1;
  for i:=1 to n do
   begin
     f[i]:=b[h]+a[i];
     work(i);
   end;
  writeln(f[n]);
  close(input); close(output);
end.
View Code

也可以用堆來優化,每次取的時候判斷根節點是否超出范圍,超出則刪除,繼續判斷,直到符合要求。

代碼2(DP+堆):

program fire;
var
  f,a,b,g:array[0..2000001]of longint;
  n,i,m,t:longint;
function get(x:longint):longint;
var i,s,tmp:longint;
begin
  while (x-g[1]>m)and(t>1) do
    begin
      b[1]:=b[t]; g[1]:=g[t];t:=t-1;
      i:=1;
      while (i*2<=t)or(i*2+1<=t) do
        begin
          if (i*2+1>t)or(b[i*2]<b[i*2+1]) then s:=i*2 else s:=i*2+1;
          if b[i]>b[s] then
           begin
             tmp:=b[i]; b[i]:=b[s]; b[s]:=tmp;
             tmp:=g[i]; g[i]:=g[s]; g[s]:=tmp;
             i:=s;
           end else break;
        end;
    end;
  get:=b[1];
end;
procedure put(x:longint);
var s,tmp:longint;
begin
  t:=t+1;b[t]:=f[x];g[t]:=x; s:=t;
  while (s<>1)and(b[s div 2]>b[s]) do
    begin
      tmp:=b[s div 2]; b[s div 2]:=b[s]; b[s]:=tmp;
      tmp:=g[s div 2]; g[s div 2]:=g[s]; g[s]:=tmp; s:=s div 2;
    end;
end;
begin
  assign(input,'fire.in');
reset(input);
assign(output,'fire.out');
rewrite(output);
  readln(n,m);
  for i:=1 to n do
   read(a[i]);
  n:=n+1;
  f[0]:=0; b[1]:=0;g[1]:=0; t:=1;
  for i:=1 to n do
   begin
     f[i]:=get(i)+a[i];
     put(i);
   end;
  writeln(f[n]);
  close(input); close(output);
end.
View Code

 


注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
粤ICP备14056181号  © 2014-2021 ITdaan.com