欢迎神犇吊打|

Hanx16Msgr

园龄:2年8个月粉丝:12关注:3

2022-07-11 15:28阅读: 39评论: 0推荐: 0

P2466 [SDOI2008] Sue 的小球

P2466 [SDOI2008] Sue 的小球

Luogu P2466

题目描述

Sue 和 Sandy 最近迷上了一个电脑游戏,这个游戏的故事发在美丽神秘并且充满刺激的大海上,Sue 有一支轻便小巧的小船。然而,Sue 的目标并不是当一个海盗,而是要收集空中漂浮的彩蛋,Sue 有一个秘密武器,只要她将小船划到一个彩蛋的正下方,然后使用秘密武器便可以在瞬间收集到这个彩蛋。然而,彩蛋有一个魅力值,这个魅力值会随着彩蛋在空中降落的时间而降低,Sue 要想得到更多的分数,必须尽量在魅力值高的时候收集这个彩蛋,而如果一个彩蛋掉入海中,它的魅力值将会变成一个负数,但这并不影响 Sue 的兴趣,因为每一个彩蛋都是不同的,Sue 希望收集到所有的彩蛋。

然而 Sandy 就没有 Sue 那么浪漫了,Sandy 希望得到尽可能多的分数,为了解决这个问题,他先将这个游戏抽象成了如下模型:

将大海近似的看做 x 轴,以 Sue 所在的初始位置作为坐标原点建立一个竖直的平面直角坐标系。

一开始空中有 N 个彩蛋,对于第 i 个彩蛋,他的初始位置用整数坐标 (xi,yi) 表示,游戏开始后,它匀速沿 y 轴负方向下落,速度为 vi 单位距离/单位时间。Sue 的初始位置为 (x0,0),Sue 可以沿 x 轴的正方向或负方向移动,Sue 的移动速度是 1 单位距离/单位时间,使用秘密武器得到一个彩蛋是瞬间的,得分为当前彩蛋的 y 坐标的千分之一。

现在,Sue 和 Sandy 请你来帮忙,为了满足 Sue 和 Sandy 各自的目标,你决定在收集到所有彩蛋的基础上,得到的分数最高。

输入格式

第一行为两个整数 N, x0 用一个空格分隔,表示彩蛋个数与 Sue 的初始位置。

第二行为 N 个整数 xi,每两个数用一个空格分隔,第 i 个数表示第 i 个彩蛋的初始横坐标。

第三行为 N 个整数 yi,每两个数用一个空格分隔,第 i 个数表示第 i 个彩蛋的初始纵坐标。

第四行为 N 个整数 vi,每两个数用一个空格分隔,第 i 个数表示第 i 个彩蛋匀速沿 y 轴负方向下落的的速度。

输出格式

一个实数,保留三位小数,为收集所有彩蛋的基础上,可以得到最高的分数。

样例 #1

样例输入 #1
3 0
-4 -2 2
22 30 26
1 9 8
样例输出 #1
0.000

提示

对于 30% 的数据, N20

对于 60% 的数据, N100

对于 100% 的数据,104xi,yi,vi104N1000

Solution

首先需要知道的是,当经过一个彩蛋时肯定是会将彩蛋捡起来的,因为如果不捡,如果等之后再来捡的话这个彩蛋带来的贡献会减少,反而不如经过就直接捡起(这道题 vi 的数据范围应该为非负数,因为如果 vi 可以为负,那么可以一直等待彩蛋向上飘,这样的话最大值是无穷大,显然不应该出现)。

因为每个彩蛋在输入时是无序的,所以在处理前应该先把彩蛋按照 x 为关键字进行排序,以保证收集的彩蛋是连续的。

考虑动态规划,设 f[i][j]ij 可以得到的最大分值,但是很快我们可以发现推不出转移方程,因为 f[i][j] 的来源太多,并且因为时间流逝彩蛋下落带来的损失也是无法计算的,所以这种方法是行不通的。

换一个方向,将 f[i][j] 设为 ij 损失的最小值,但也会发现,因为我们不知道此时的人在哪里,所以无法计算具体时间。

因为收集完 ij 彩蛋的一瞬间,人只可能在区间的最左端或者是最右端上,即区间的端点,因此我们可以设出新的状态转移方程。

f[i][j] 表示 ij 彩蛋捡完后人在 i 时损失的最小值, g[i][j] 表示 ij 彩蛋捡完后人在 j 时损失的最小值。考虑状态的来源,对于 f[i][j],人只可能从 i+1 移动一步到达 i ,或者是从 j 跨越整个区间到达 i 。很容易发现,只有这两种来源是可以更新 f[i][j] 的。据此,可以推导出对于 f[i][j] 的状态转移方程:

f[i][j]=min{f[i+1][j]+(Sum(1,i)+Sum(j+1,n))×(xi+1xi),g[i+1][j]+(Sum(1,i)+Sum(j+1,n)×(xjxi))}

类似地,也可以推出 g[i][j] 的状态转移方程。

根据这些信息,代码就很好写出来了。

需要注意的是,因为数据范围的原因, f[i][j]g[i][j] 可能会爆 int ,因此需要开 long long

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<limits.h>
#include<cmath>
#define mem(a,b) memset(a,b,sizeof(a));
using namespace std;
template<typename T> void read(T &k)
{
 	k=0;
	T flag=1;char b=getchar();
	while (b<'0' || b>'9') {flag=(b=='-')?-1:1;b=getchar();}
	while (b>='0' && b<='9') {k=(k<<3)+(k<<1)+(b^48);b=getchar();}
	k*=flag;
}
const long long _SIZE=1e3;
struct BALL{
	long long x,y,v;
}ball[_SIZE+5];
bool cmp(BALL x,BALL y)
{
	return x.x<y.x;
}
long long n,sx;
long long f[_SIZE+5][_SIZE+5],g[_SIZE+5][_SIZE+5];
long long sum[_SIZE+5];
long long SumCount(long long l,long long r) {return sum[r]-sum[l-1];}
long long ans=0;
int main()
{
	mem(f,0x7f);
	mem(g,0x7f);
	read(n),read(sx);
	for (long long i=1;i<=n;i++) read(ball[i].x);
	for (long long i=1;i<=n;i++) {read(ball[i].y);ans+=ball[i].y;}
	for (long long i=1;i<=n;i++) read(ball[i].v);
	n++;ball[n].x=sx;
	sort(ball+1,ball+n+1,cmp);
	for (long long i=1;i<=n;i++) sum[i]=ball[i].v+sum[i-1];
	for (long long i=1;i<=n;i++) if (ball[i].x==sx) f[i][i]=g[i][i]=0;
	for (long long len=2;len<=n;len++)
	{
		for (long long i=1;i+len-1<=n;i++)
		{
			long long j=i+len-1;
			f[i][j]=min(f[i][j],f[i+1][j]+(SumCount(1,i)+SumCount(j+1,n))*(ball[i+1].x-ball[i].x));
			f[i][j]=min(f[i][j],g[i+1][j]+(SumCount(1,i)+SumCount(j+1,n))*(ball[j].x-ball[i].x));
			g[i][j]=min(g[i][j],f[i][j-1]+(SumCount(1,i-1)+SumCount(j,n))*(ball[j].x-ball[i].x));
			g[i][j]=min(g[i][j],g[i][j-1]+(SumCount(1,i-1)+SumCount(j,n))*(ball[j].x-ball[j-1].x));
		}
	}
	printf("%.3lf\n",(double)(ans-min(g[1][n],f[1][n]))/1000);
	return 0;
}

posted @   Hanx16Msgr  阅读(39)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起