解题报告 帮忙

帮忙

 

 

 

 

 

在高考研讨会上,随着 z 同学对 zn 了解的深入,发现她不仅是一个善良的女孩子,还 是一个很勤劳的女生哇塞这样棒的女生在21世纪真是太难遇到了要是可以娶到这样的 女生,小 z 陷入了无限的 yy 

高考研讨会进入了尾声,校长一声令下,搬椅子喽~~~~,只见平时道貌岸然的那些所

谓的好学生们一听要干活全跑了,shit~~!!操场上只剩 zn  z 同学了,面对成千上万的椅子, 肿么办~~ z 突然意识到现在正是奥赛课啊于是他想到了 hzoi2009的兄弟们果然,当小 跑到机房说明来意后,hzoi2009的兄弟们(还有个妹子当时貌似在考试,但都没有半点 迟疑,立马跟随小 z 下楼(小 z 一直把这件事记在心中,现在回想起来还有点小感动哈)

 

 

 

面对成千上万的椅子当然不能用蛮力一个一个的搬了校长大人提供了一种搬运车。 椅子们是排成一排的搬运车一次必须搬不少于k 个的连续的椅子消耗的机油为所搬椅子重量的平均值(总重量比椅子个数)。小 z 想知道搬一次最多能耗费多少机油,好向校长报销机油费

输入:

第一行:整数 n(代表椅子数)与 k(一次搬的最少的椅子数) 接下来 n 行,每行一个整数 w i,代表椅子的重量

 

 

 

搬一次可能耗费的最多的机油数*1000(结果取整) 样例输入

10 6

6

4

2

10

3

8

 

5

9

4

1

 

 

 

 

6500

 

 

 

 

对于30%的数据 0<k<N<=1000

对于100%的数据 0<k<n<100 000

0<Wi<=2000

 

 

 

注意,此处 ans 取整的时候一定要用 trunc 而不能四舍五入,血的教训啊!

 

SueMiller:直接枚举这个区间, 40 

用一个很没技术含量的,根本不对的动规:用 f[i] 表示到第 个节点,而且选取这个节点,合法的一段数串的最大的平均值,用 g[i] 表示这一个合法的书穿的较小边界。

然后可以分析,每一个数串的最优值极有可能是以下两种情况之一:从这个数串开始,往前找长度为 的数串的平均值;f[i-1] 的数串再加上这一个。

然后,就可以转移了。 80

代码:(SueMiller

program ACRush;

 

var n,k:longint;

    a,g:array[0..100000]of longint;

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

    i,j:longint;

    ans:longint;

 

begin

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

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

 

  readln(n,k);

  fillchar(a,sizeof(a),0);

  fillchar(f,sizeof(f),0);

  for i:=1 to n do

    begin

      readln(a[i]);

      a[i]:=a[i]+a[i-1];

    end;

 

{  ans:=0;

  for i:=0 to n-k do

    for j:=i+k to n do

      begin

        if trunc((a[j]-a[i])/(j-i)*1000)>ans then ans:=trunc((a[j]-a[i])/(j-i)*1000);

      end;

 

  writeln(ans);  }   ------>这是枚举,40

 

  fillchar(g,sizeof(g),0);

  f[k]:=trunc(a[k]/k*1000);

  g[k]:=1;

  ans:=f[k];

  for i:=k+1 to n do

    begin

      if (a[i]-a[g[i-1]-1])/(i-g[i-1]+1) > (a[i]-a[i-k])/k then

        begin

          f[i]:=trunc((a[i]-a[g[k-1]-1])/(i-g[k-1]+1)*1000);

          g[i]:=g[i-1];

        end

      else begin

        f[i]:=trunc((a[i]-a[i-k])/k*1000);

        g[i]:=i-k+1;

      end;

      if f[i]>ans then ans:=f[i];

    end;

 

  writeln(ans);

 

  close(input);close(output);

end.

 

 

然后,我们分析这个根本不对的算法,他不对的地方就在于这个东西有后效性,有可能你再上一个最优数串中,加入了一个出奇的小的值,导致它的平均值变小了许多,而在你往前追溯 个数的时候,可能往前的 k+1 个是一个介于 f[i-1] 和 f[i] 之间的值。也就是说,这一个值,对于 f[i-1] 来说,是在最优值之下的,不能选取。但是,对于 f[i] 来说,它却是可以用于更新的最优值,这种情况就挂了。

 

所以,对于这个算法的改进就是,再向前比较寻找,但这样就成了 On^2

 

然后,理所当然需要优化。

计算一下,将每一个区间的数值总和和这个区间的数值个数分别当做 轴和 轴,轻易可以看出,我们要求的就是他的斜率,所以斜率越大越好。

这样一来,可以每次掐出一个 长度的区间,如果它的斜率比当前值更优,就更新当前值,将已知数串的上界改变,就这样依次将斜率往上叠加,利用一下导数的思想,然后及时更新最优值。

其实这个算法,是一种算法的变形:求不限长度的最大子序和:从第一个值开始,依次往后叠加,如果大于当前值就更新当前值,如果小于 就转赋值成 ,这个只不过对长度加了一个限制,外加变成了斜率。

 

代码 (LYF

var

  s:array[0..100000] of longint;

  n,m,i,j,k:longint;

  ans:double;

 

begin

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

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

  readln(n,m);

  for i:=1 to n do

    begin

      readln(k);

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

    end;

  for i:=0 to n-m do

    begin

      k:=i+m;

      if (s[k]-s[i])*(k-j)>(s[k]-s[j])*(k-i) then  j:=i;

      if (s[k]-s[j])/(k-j)*1000>ans then ans:=(s[k]-s[j])/(k-j)*1000;

    end;

  writeln(trunc(ans));

  close(input); close(output);

End.

 

 

 

然后,如果这个听不懂,还有一个比较好理解的。

二分答案,二分那个平均值是多少(注意 0<Wi<=2000 ),然后判断,将各个项都减去这个平均值,再求出每一个区间的各个项的和,如果有比这个值大的就将这个值往大里找,反之往小里找,就这样,一直到上下界重合,就可以了。

 

代码 (Dsqwwe

program dsqwwe;

  var

    sum,b,f:array[0..100005] of extended;

    a:array[0..100005] of longint;

    l,r,mid,max,min:extended;

    n,m,i:longint;

  function maxx(a,b:extended):extended;

    begin

      if a>b then exit(a)

       else exit(b);

    end;

 

  function check(val:extended):boolean;

    var

      i:longint;

    begin

      fillchar(sum,sizeof(sum),0);

      for i:=0 to n do

       f[i]:=-maxlongint;

      for i:=1 to  n do

       begin

         b[i]:=a[i]-val;

         sum[i]:=sum[i-1]+b[i];

       end;

      f[m]:=sum[m];

      if f[m]>-0.00000001 then exit(true);

      for i:=m+1 to n do

       begin

         f[i]:=maxx(f[i-1]+b[i],sum[i]-sum[i-m]);

         if f[i]>-0.0000001 then exit(true);

       end;

      exit(false);

    end;

 

  begin

    assign(input,'help.in');

    reset(input);

    assign(output,'help.out');

    rewrite(output);

 

    readln(n,m);

    min:=maxlongint; max:=0;

    for i:=1 to n do

     begin

       readln(a[i]);

       a[i]:=a[i]*1000;

       if a[i]>max then max:=a[i];

       if a[i]<min then min:=a[i];

     end;

    l:=min; r:=max;

    while r-l>=0.0000001 do

     begin

       mid:=(r+l)/2;

       if check(mid) then

        l:=mid

       else r:=mid;

     end;

    mid:=mid+0.000001;

    writeln(trunc(mid));

 

    close(input);

    close(output);

  end.

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