• 解题报告 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.

     

     

  • 相关阅读:
    别人走的路-1
    抽象类跟接口的区别
    一个类实现多个接口的demo
    servlet+jdbc+javabean其实跟ssh差不多
    什么是shell
    设置cookie倒计时让让表单自动提交
    变量、基本数据类型
    编程语言、添加环境变量、变量
    Java优先队列一些问题
    JavaScript学习-JSON
  • 原文地址:https://www.cnblogs.com/SueMiller/p/2207761.html
Copyright © 2020-2023  润新知