JZOJ(C组)雾雨魔理沙

题目:在幻想乡,雾雨魔理沙是住在魔法之森普通的黑魔法少女。话说最近魔理沙从香霖堂拿到了升级过后的的迷你八卦炉,她迫不及待地希望试试八卦炉的威力。在一个二维平面上有许多毛玉(一种飞行生物,可以视为点),每个毛玉具有两个属性,分值value和倍率mul。八卦炉发射出的魔法炮是一条无限长的直线形区域,可以视为两条倾斜角为α的平行线之间的区域,平行线之间的距离可以为任意值,如下图所示:
  
  蓝色部分上下两条长边之间就是这次八卦炉的攻击范围,在蓝色范围内的毛玉(红点)属于该此被击中的毛玉,如果一个毛玉刚好在边界上也视为被击中。毛玉击中以后就会消失,每次发射八卦炉得到分值是该次击中毛玉的分值和乘上这些毛玉平均的倍率,设该次击中的毛玉集合为S,则分值计算公式为:
  Score = SUM{value[i] | i 属于 S} * SUM{mul[i] | i 属于 S} / |S|

  其中|S|表示S的元素个数。魔理沙将会使用若干次八卦炉,直到把所有毛玉全部击中。任意两次攻击的范围均不重叠。最后得到的分值为每次攻击分值之和。现在请你计算出能够得到的最大分值。

分析:这道题目像一次函数,用tan(α)就可表示Y=KX+B中的K值了(具体用数学演算,找到函数与X坐标轴交点后再在函数上任意找一点来推断就可以了。)再用DP,状态转移方程:f[i]:=max(f[i],f[j]+(sum1[i]-sum1[j])*(sum2[i]-sum2[j])/(sum[i]-sum[j]));


附上代码:

uses math;

const
  maxn=3000;

var
  x,y,value,mul,sum,sum1,sum2:array [0..maxn] of longint;
  f,compare:array [0..maxn] of double;
  flag:array [0..maxn] of boolean;
  n,angle,times:longint;
  real1:double;

procedure init;
var
  i:longint;
begin
  readln(n);
  for i:=1 to n do
    readln(x[i],y[i],value[i],mul[i]);
  readln(angle);
  real1:=tan(angle*pi/180);
  for i:=1 to n do
    compare[i]:=y[i]-real1*x[i];
end;

procedure qsort(l,r:longint);
var
  i,j,temp:longint;
  test,mid:double;
begin
  i:=l;
  j:=r;
  mid:=compare[(i+j) div 2];
  repeat
    while compare[i]<mid do
      inc(i);
    while compare[j]>mid do
      dec(j);
    if i<=j then
      begin
        test:=compare[i];
        compare[i]:=compare[j];
        compare[j]:=test;
        temp:=x[i];
        x[i]:=x[j];
        x[j]:=temp;
        temp:=value[i];
        value[i]:=value[j];
        value[j]:=temp;
        temp:=y[i];
        y[i]:=y[j];
        y[j]:=temp;
        temp:=mul[i];
        mul[i]:=mul[j];
        mul[j]:=temp;
        inc(i);dec(j);
    end;
  until i>j;
  if i<r then
    qsort(i,r);
  if l<j then
    qsort(l,j);
end;

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

procedure prepare;
var
  i,j:longint;
begin
  qsort(1,n);
  for i:=1 to n do
    if not flag[i] then
      begin
        flag[i]:=true;
        inc(times);
        inc(sum[times]);
        sum1[times]:=sum1[times]+value[i];
        sum2[times]:=sum2[times]+mul[i];
        for j:=i+1 to n do
          if not flag[j] then
            begin
              if compare[j]=compare[i] then
                begin
                  flag[j]:=true;
                  inc(sum[times]);
                  sum1[times]:=sum1[times]+value[j];
                  sum2[times]:=sum2[times]+mul[j];
                end;
            end;
     end;
end;

procedure main;
var
  i,j:longint;
begin
  for i:=2 to times do
    begin
      sum[i]:=sum[i]+sum[i-1];
      sum1[i]:=sum1[i]+sum1[i-1];
      sum2[i]:=sum2[i]+sum2[i-1];
    end;
  for i:=1 to times do
    for j:=0 to i-1 do
      f[i]:=max(f[i],f[j]+(sum1[i]-sum1[j])*(sum2[i]-sum2[j])/(sum[i]-sum[j]));
  writeln(f[times]:0:3);
end;

begin
  init;
  prepare;
  main;
end.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值