天堂

题目描述

每一个要上天堂的人都要经历一番考验,当然包括小X,小X开始了他进入天堂的奇异之旅。地狱有18层,天堂竟然和地狱一样,也有很多很多层,天堂共有N层。从下到上依次是第1,2,3,…,N层,天堂的每一层都是一个延伸无限远的地板,在地板上人可以任意走动,层与层之间是平行关系,每一层的地板都是由人不能穿过的物质构成,幸好每一层地板上有且仅有1个人可以通过的洞口。
我们可以把小X和洞口,还有下面提到的气球店都看成点,坐标是二维的。小X开始在第1层的(0,0).
小X的重量为M,第i层与第i+1层之间的特殊气体能浮起的重量为Wi ,每一层的地面上散落了若干个气球店,多个气球店可以在同一点,每个气球可以浮起的重量是1,去一个气球店一次只能领取一个气球,不能连续在一个气球店领取气球,当然你可以在两个气球店之间来回跑,每个气球店供应的气球都是无限多的。第i层的气球只能在第i层进入第i+1层时使用,当小X在第i层,只有站到了第i+1层洞口的位置(在其它位置不会浮起),并且自身的重量小于等于气球和特殊气体浮起重量的总和,才可以进入第i+1层。小X想知道他要到达第N层走过的长度最少是多少?题目保证有解。
输入文件
第1行: 三个正整数N,M,Q(Q表示气球店)
第2行: 共2*(N-1)个整数,每两个数描述1个洞口坐标,第i对xi,yi表示第i+1层的洞口位置(xi,yi)。
第3行: 共N-1个整数,第i个数为Wi。
往后Q行,每行三个整数x,y,z , 表示第Z层有一个气球店,坐标为(x,y)
输出文件
1个实数L,保留两位小数,表示小X最少要走的长度。
样例输入
3 10 4
0 0 1 2
9 0
0 1 1
2 3 1
0 1 2
1 1 2
样例输出
13.00
注释
【样例解释】
在第一层从(0,0)出发到(0,1)取得1个气球并返回(0,0)即可到达第二层。长度:2.00
在第二层,从(0,0)到(0,1)领取气球,再到(1,1)领取气球,两个点来回跑,第5次到达(1,1)时恰好气球数达到10,走到(1,2)即可到达第3层终点。长度:11.00
总长度:13.00
【数据范围】
2<=N<=100
每层的气球店数目不超过50。
0<=M<=100, 0<=Wi<=100
坐标-3000<=x,y<=3000


思路

这个DPP。。。。对于每一层的答案都是独立的,所以我们对每一层进行DP。
f【i,j】表示位于第j个气球店时已经收集了i个气球所花费的最优距离

代码

type
  rec=record
   x,y:integer;
   end;
var
    enter:array[1..100]of rec;  //入口位置
    qiu:array[1..100,1..50]of rec;//每层每个气球店的位置
    num:array[1..100]of integer; //每层气球店的数量
    w:array[1..100]of integer;  
    f:array[1..110,1..50]of double;
    z,x,y,i,j,n,m,q:longint;
    sum:double;
function min(a,b:real):real;
begin
  if a<b then exit(a);exit(b);
end;

function dis(x1,y1,x2,y2:integer):double;
begin
  exit(sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2)));
end;

function find(k:integer):real;
var t,need,i,j:longint;
    ans:real;
    now:rec;    //第k层的初始位置
begin
  for i:=1 to 110 do
    for j:=1 to 50 do f[i,j]:=maxlongint;
  if k=1 then begin now.x:=0;now.y:=0;end
         else now:=enter[k-1];
  need:=m-w[k];
  ans:=maxlongint;
  if need<=0 then   //不需要拿气球时
    exit(dis(now.x,now.y,enter[k].x,enter[k].y));
  for i:=1 to num[k] do  //初始状态
    f[1,i]:=dis(now.x,now.y,qiu[k,i].x,qiu[k,i].y);
  if need=1 then     //处理只需一个气球的情况
    begin
      for i:=1 to num[k] do
         ans:=min(ans,f[1,i]+dis(qiu[k,i].x,qiu[k,i].y,enter[k].x,enter[k].y));
        exit(ans);
    end;

  for i:=2 to need do   //递推答案
   for j:=1 to num[k] do
    for t:=1 to num[k] do
      if j<>t then    //千万别忘这个条件
      f[i,j]:=min(f[i,j],f[i-1,t]+dis(qiu[k,t].x,qiu[k,t].y,qiu[k,j].x,qiu[k,j].y));

  for i:=1 to num[k] do ans:=min(ans,f[need,i]+dis(qiu[k,i].x,qiu[k,i].y,enter[k].x,enter[k].y));
  exit(ans);

end;

begin
  assign(input,'heaven.in');
  assign(output,'heaven.out');
  reset(input);
  rewrite(output);
  readln(n,m,q);
  for i:=1 to (n-1) do
     read(enter[i].x,enter[i].y);
  for i:=1 to n-1 do read(w[i]);
  for i:=1 to q do
    begin
      readln(x,y,z);
      inc(num[z]);
      qiu[z,num[z]].x:=x;
      qiu[z,num[z]].y:=y;
    end;
  sum:=0;
  for i:=1 to n-1 do   //枚举到n-1就ok了!!!
    sum:=sum+find(i);
  write(sum:0:2);
  close(input);
  close(output);
end.
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值