解题报告 B_station

1.        题目

B_station

在离著名的国家Berland不远的地方,有一个水下工作站。这个工作站有N层。已知:是第i层装有Wi的水,最多可以容纳Li的水,恐怖分子炸毁第i层的代价是Pi。第i层一旦被炸毁,该层所有的水都将倾泻到第i+1层。如果某一层的水量超过了它的容量(即Li),那么该层就将自动被毁坏,所有的水也会倾泻到下一层。

Pivland的恐怖分子想要用最少的钱毁掉第N层,现在他雇佣你来计算,需要炸毁哪些层。

输入:第一行有一个自然数N(1<=n<=15000)。接下来的N行,每行3个整数Wi, Li, Pi(0<=Wi,Li,Pi<=15000)。

输出:输出需要炸毁的层的编号。

样例Input                    样例output

3                                  1

1000 1000 1                  2

0 1000 2 

2 10 100 

 

2.        题目实质

这个题讲的也很明白。

3.        算法

首先,不要一看见最优值就认为是动规,这个题找状态......

其实,他是一个枚举。

选择枚举对像:不妨设恐怖分子炸毁的最高层是第p层(第一层是最高层,第N层是最底层)。

因为恐怖分子的目标是毁灭第N层,所以水必须从第p层一直泻下去。如果存在一个i,满足Wp+Wp+1+…+Wi-1+Wi<=Li,也就是说前面几层的水全部泄下来也无法把第i层自动冲毁,那么就必须要使用炸药把它炸开了。

考察: Wp+Wp+1+…+Wi-1+Wi<=Li
这个式子要求一段的数字和,为了快速,用前缀和:
   令Si=W1+W2+…+Wi(特别的S0=0)。

上式可表示成: Si-Sp-1 <=Li

所以恐怖分子需要炸毁的层的集合就是Bomb[p]={p}∪{i |i>p Si-Sp-1<=Li}

我们枚举p,然后看哪些层需要炸毁。这样就得到了一个O(n2)的算法。 

 

然后,这里主要讲一下优化:

剪枝:在你枚举的时候,在内层循环中记录一下当前炸了这么多层已经花了多少钱,当这个数目大于已经记录的最小值时, break 。(这样就能过)

记忆化:当搜到了一层的时候,已经可以自己冲垮而不用花钱,那么显然将炸的最高层再往上抬,它还是不用花钱,此时就需要将它记录下来,以后不用搜了。也可以在每次需要花钱的时候,记录下还有多少水就可以不用花钱了,到 0 的时候也就不用再搜了。

单调性优化:(大根堆、二分查找、单调队列 etc)

Bomb[p]={p}∪{i |i>p Si-Sp-1<=Li}  è

    令Qi=Si-Li,那么:

Bomb[p]={p}∪{i |i>p Qi<=Sp-1}

注意到Sp是随着p的增加而递增的(单调性),观察两个集合:

Bomb[p]={p}∪{i |i>p Qi<=Sp-1}

Bomb[p-1]={p-1}∪{i |i>p-1 Qi<=Sp-2}

因为Sp-2<=Sp-1,所以{i |i>p-1 Qi<=Sp-2}包含于 Bomb[p],也就是说,Bomb[p-1]是在Bomb[p]的基础上,通过以下操作得到的:

删除所有的i∈Bomb[p],Qi>Sp-2。(所以找最大值,删除判断就行了)

添加p-1。

针对这两种操作,我们可以使用大根堆(Heap)(单调队列、二分查找什么的都行)。从大到小枚举p,对于每一个p,执行:

Step1. 如果堆的根i满足Qi >Sp-2,那么删除根;否则转Step3。

Step2. 转Step1。

Step3. 添加p-1。

每层至多进堆一次、出堆一次,所以总的时间复杂度是O(nlogn)。对于n<=15000的范围完全能够胜任了。

 

4.        注意事项

  不要一看到最优值就写动规,也适当想一想基础算法。

注意观察数据范围,不要每次一上来就写最厉害(一般也是最难写)的优化,优化优化的不好就会变成退化了,像这个题,三个优化中有一个就可以 AC 。

5.        代码

只用了第一个优化,刚好能过(SueMiller

var n:longint;

    i,j,k:longint;

    w,l,p:array[0..15001]of longint;

    v,vv:array[0..15001]of boolean;

    ans,temp,co:longint;

 

begin

  assign(input,'station.in');reset(input);

  assign(output,'station.out');rewrite(output);

 

  fillchar(w,sizeof(w),0);

  fillchar(l,sizeof(l),0);

  fillchar(p,sizeof(p),0);

  readln(n);

  for i:=1 to n do

    begin

      readln(w[i],l[i],p[i]);

    end;

 

  if w[n]>l[n] then begin

    writeln(0);

    close(input);close(output);

    halt;

  end;

 

  temp:=w[n];

  k:=n;

  while temp<=l[n] do

    begin

      dec(k);

      temp:=temp+w[k];

    end;

  ans:=p[n];

 

  fillchar(v,sizeof(v),false);

  fillchar(vv,sizeof(vv),false);

  vv[n]:=true;

  for i:=k downto 1 do

    begin

      temp:=w[i];

      if temp<=l[i] then

        begin

          co:=p[i];

          v[i]:=true;

        end;

 

      for j:=i+1 to n do

        begin

          if temp+w[j]>l[j] then begin

            temp:=temp+w[j];

            continue

          end

          else begin

            co:=co+p[j];

            v[j]:=true;

            temp:=temp+w[j];

            if co>ans then begin

              fillchar(v,sizeof(v),false);

              break;

            end;

          end;

        end;

      if co<ans then

        begin

          vv:=v;

          ans:=co;

          fillchar(v,sizeof(v),false);

        end;

    end;

 

  writeln(ans);

  for i:=1 to n do

    if vv[i] then write(i,' ');

  writeln;

//  temp:=0;

//  for i:=1 to n do

//    if vv[i] then begin

//      temp:=temp+p[i];

//    end;

//  writeln(temp,'   ##');

  close(input);close(output);

end.

 

 

 

 

大根堆 (Liukeke

program liukeke;

var

  w,p,l,s,q:array[0..15001] of longint;

  f:array[0..15001] of longint;

  ans:array[0..15001] of longint;

  m,ans0,anscost,cost,i,n:longint;

 

procedure downsift(x:longint);

var

  k,temp:longint;

begin

  temp:=f[x];

  k:=x<<1;

  while k<=m do

  begin

    if(k<m)and(q[f[k]]<q[f[k+1]]) then inc(k);

if q[temp]<q[f[k]] then

begin

  f[x]:=f[k];

  x:=k;

  k:=x<<1;

end

else break;

  end;

  f[x]:=temp;

end;

 

procedure upsift(x:longint);

var

  k,temp:longint;

begin

  temp:=f[x];

  k:=x>>1;

  while k>0 do

  begin

if q[temp]>q[f[k]] then

begin

  f[x]:=f[k];

  x:=k;

  k:=x>>1;

end

else break;

  end;

  f[x]:=temp;

end;

 

procedure sort(l,r:longint);

var

  i,j,mid,temp:longint;

begin

  i:=l;j:=r;mid:=ans[(l+r)>>1];

  repeat

    while ans[i]<mid do inc(i);

while ans[j]>mid do dec(j);

if i<=j then

begin

  temp:=ans[i];

  ans[i]:=ans[j];

  ans[j]:=temp;

  inc(i);

  dec(j);

end;

  until i>j;

  if l<j then sort(l,j);

  if i<r then sort(i,r);

end;

 

begin

  assign(input,'station.in');reset(input);

  assign(output,'station.out');rewrite(output);

  readln(n);

  for i:=1 to n do

    readln(w[i],l[i],p[i]);

  for i:=1 to n do

    s[i]:=s[i-1]+w[i];

  for i:=1 to n do

    q[i]:=s[i]-l[i];

 

  m:=1;

  f[1]:=n;

  cost:=p[n];

  anscost:=p[n];

  for i:=n downto 2 do

  begin

    //

    while (q[f[1]]>s[i-2])and(m>0) do

begin

  dec(cost,p[f[1]]);

  f[1]:=f[m];

  dec(m);

  downsift(1);

    end;

//

inc(m);

f[m]:=i-1;

upsift(m);

//

inc(cost,p[i-1]);

if cost<anscost then

begin

  anscost:=cost;

  ans:=f;

  ans0:=m;

end;

  end;

 

 //outit;

  sort(1,ans0);

  writeln(anscost);

  for i:=1 to ans0 do

    write(ans[i],' ');

  close(input);

  close(output);

end.

 

 

原文地址:https://www.cnblogs.com/SueMiller/p/2207761.html