☆抓鱼

 
 
From KQZXCMH
抓鱼
 
   

 

 

 

 

背景 Background

 

 

清北学堂TYVJ9月模拟赛试题第二题

 

 

 

 

 

 

 

描述 Description

 

 

  “抓鱼”是一款游戏,全名叫做“Ships catch fish on a river”。
  游戏中河被抽象成一个宽度为1,长度为N的矩形,从左到右每一个格子被从1~N标号。每个格子中都有一些鱼,第i格有ai条鱼。
  河上有M条船,每条船都有一个固定的长度Di,且船只能停在整数格上。两艘船之间不能有重叠,比如一艘船停在1到5格,还有一艘船停在5到6格,这就是非法的,因为5这格重叠了。船为了停止,需要下锚,每条船只能在Bi格上下锚,且船身必须覆盖下锚的这格。
  现在我们定义,所有被船覆盖的格子处的鱼,都将被抓住。
  你需要编一个程序来求出,最多可以抓住多少条鱼。

 

 

 

 

 

 

 

输入格式 Input Format

 

 

第一行一个数N。
第二行N个数,第i个数是ai。
第三行一个数M。
接下来M行,第i+3行描述Bi和Di。

 

 

 

 

 

 

 

输出格式 Output Format

 

 

输出一个数,表示最多可以抓到的鱼的数目。

 

 

 

 

 

 

 

样例输入 Sample Input [复制数据]

 

 

 

 

 

 

 

 

 

样例输出 Sample Output [复制数据]

 

 

 

 

 

 

 

 

 

时间限制 Time Limitation

 

 

时间限制 1s

 

 

 

 

 

 

 

注释 Hint

 

 

数据规模
  对于30% 的数据 N≤100;
  对于100% 的数据 1≤N≤100 000,1≤M≤N,1≤ai≤100。Bi和Di保证,总存在合法的方案,使得所有船都可以在河上,且不冲突。

 

【解题报告】

先把船的抛锚点排个序。

f[i,j]表示前i只船且第i只船最后一点在j方格所取到的最优值。

f[i,j]:=max{f[i-1,k]+sum[j]-sum[j-c[i]]};

j和k的枚举范围需要好好琢磨一下。

program p1656;
var
  ans,j,k,n,m,i:longint;
  f:array[0..1,0..100001]of longint;
  b,c,a,sum:array[0..100001]of longint;

procedure init;
begin
  assign(input,'p1656.in');
  reset(input);
  assign(output,'p1656.out');
  rewrite(output);
end;

procedure outit;
begin
  close(input);
  close(output);
end;

function min(a,b:longint):longint;
begin
  if a<b then exit(a);
  exit(b);
end;

function max(a,b:longint):longint;
begin
  if a>b then exit(a);
  exit(b);
end;

procedure qsort(l,r:longint);
var
  i,j,x,t:longint;
begin
  i:=l; j:=r; x:=b[(l+r) shr 1];
  repeat
    while b[i]<x do inc(i);
    while b[j]>x do dec(j);
    if not (i>j) then
    begin
      t:=b[i]; b[i]:=b[j]; b[j]:=t;
      t:=c[i]; c[i]:=c[j]; c[j]:=t;
      inc(i);
      dec(j);
    end;
  until i>j;
  if i<r then qsort(i,r);
  if l<j then qsort(l,j);
end;

procedure main;
begin
  readln(n);
  a[n]:=0;
  for i:=1 to n do read(a[i]);
  for i:=1 to n do sum[i]:=sum[i-1]+a[i];

  readln(m);
  for i:=1 to n do read(b[i],c[i]);
  qsort(1,m);
  fillchar(f,sizeof(f),0);
  for i:=1 to m do
  for j:=max(c[i],b[i]) to min(b[i]+c[i]-1,n) do
  for k:=max(c[i-1],b[i-1]) to j-c[i] do
  begin
    f[i and 1,j]:=max(f[i and 1,j],f[(i-1) and 1,k]+sum[j]-sum[j-c[i]]);
  end;
  
  ans:=0;
  for i:=b[m] to b[m]+c[m]-1 do ans:=max(ans,f[m and 1,i]);
  writeln(ans);
end;

begin
  init;
  main;
  outit;
end.


 

这题数据规模不算小,于是应该想到转换方程表示,用f[i,j]表示第i条船船尾超过其锚的距离,由于不可能存在两个锚相距很远,于是j只用几百就行了;在这里,关键是转移;

g[i]表示到第i个位置时,能得到最大价值是多少;

f[i,j]=max{g[k]+sum[b[i]+j]-sum[b[i]+j-d[i]],f[i,j]}其中k在第i条船的船头左边;

g[k]=max{g[k-1],f[i,k-b[i]]};

注意:

1.更新时一定要注意枚举的范围;

2.方便起见,可以加第m+1条船,锚在n+1,宽度为1,n+1价值为0;

 

type ty=record d,b:longint;end;
var s:array[0..100000]of ty;
    sum,g:array[0..100000]of longint;
    f:array[0..100000,0..200]of longint;
    n,m,i,j,k,l,ans:longint;
function max(a,b:longint):longint;
begin
   if a>b then exit(a) else exit(b);
end;
function min(a,b:longint):longint;
begin
  if a<b then exit(a) else exit(b);
end;

procedure sort(a,b:longint);
var i,j,x:longint;
    te:ty;
begin
    if a>=b then exit;
    i:=a;j:=b;x:=s[(i+j)div 2].b;
    while i<=j do
       begin
          while s[i].b<x do inc(i);
          while s[j].b>x do dec(j);
          if i<=j then
              begin
                 te:=s[i];s[i]:=s[j];s[j]:=te;
                 inc(i);dec(j);
              end;
       end;
    sort(a,j);sort(i,b);
end;

begin
   read(n);
   for i:=1 to n do read(sum[i]);
   for i:=1 to n do sum[i]:=sum[i]+sum[i-1];
   read(m); s[m+1].b:=n+1;s[m+1].d:=1;
   s[0].b:=0;s[0].d:=1;
   for i:=1 to m do
      read(s[i].b,s[i].d);
   sort(1,m);
   for i:=1 to m do
     begin
      for j:=max(0,s[i].d-(s[i].b-s[i-1].b)) to min(s[i].d-1,s[i+1].b-s[i].b-1) do
         f[i,j]:=g[s[i].b+j-s[i].d]+sum[s[i].b+j]-sum[s[i].b+j-s[i].d];
      for j:=s[i].b to s[i+1].b -1 do
         begin
            g[j]:=g[j-1];if f[i,j-s[i].b]>g[j] then g[j]:=f[i,j-s[i].b];

         end;
     end;
   writeln(g[n]);

end.


 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值