[SDOI2008]Sue的小球

XXIX.[SDOI2008]Sue的小球

DP做多了,手感自然就出来了。

话说这题打着“小球”的名字题目中却是“彩蛋”是怎么回事

首先,这个下落速度\(v\),尽管题面中说它可能为负数,但我们想一想,这可能吗?如果是负数答案就是正无穷(可以等着这个球一直向上飞),因此排除球速为负的可能。

如果是这样的话,那么,当我们经过一个球时,随手将它射爆明显是更好的行为。因此,无论何时,我们球已经被射下的位置一定是一个包含起始点\(x_0\)的区间。

我们将所有球按照位置在\(x_0\)左边还是右边压进两个vector中(下标从\(0\)开始)。在左边的vector(设为\(v1\))中,我们按照\(x\)值从大到小排序并处理;在右边(设为\(v2\)),我们从小到大排序。同时,我们在\(v1\)\(v2\)中都压入一个\(x=x_0\)的球,方便初始化。

我们设\(f[i][j][0/1]\)表示:当前进行到\(v1\)的第\(i\)位,\(v2\)的第\(j\)位,同时位于这个区间的左/右端点的情况。我们可以提前计算出所有小球初始\(y\)值的和,这样我们只需要最小化捡球过程中球下落的距离即可。

我们设\(s[i][j]\)表示:除了\(v1\)\(i\)位和\(v2\)\(j\)位外,其它球\(1s\)内下落的距离之和(这借鉴了XII.任务安排费用提前计算的经典思想)。在实现中,这个可以直接通过前缀和做出。设\(x1[i]\)表示\(v1\)\(x\)值,\(x2[j]\)表示\(v2\)\(x\)值。

我们有

\(f[i][j][0]=f[i-1][j][0]+\Big|x1[i-1]-x1[i]\Big|*(s[i-1][j])\)

\(f[i][j][1]=f[i][j-1][0]+\Big|x2[j-1]-x2[j]\Big|*(s[i][j-1])\)

然后因为两边可以互相走,所以还有

\(f[i][j][0]=f[i][j][1]+\Big|x2[j]-x1[i]\Big|*s[i][j]\)

\(f[i][j][1]=f[i][j][0]+\Big|x1[i]-x2[j]\Big|*s[i][j]\)

两种转移取\(\min\)即可。

复杂度\(O(n^2)\)

代码:

#include<bits/stdc++.h>
using namespace std;
int n,X,s1[1010],s2[1010],sy,f[1010][1010][2],sv;
struct node{
	int x,y,v;
	node(int a=0,int b=0,int c=0){x=a,y=b,v=c;}
}b[1010];
vector<node>v1,v2;
bool cmp1(const node &x,const node &y){
	return x.x<y.x;
}
bool cmp2(const node &x,const node &y){
	return x.x>y.x;
}
int main(){
	scanf("%d%d",&n,&X),memset(f,0x3f3f3f3f,sizeof(f));
	for(int i=1;i<=n;i++)scanf("%d",&b[i].x);
	for(int i=1;i<=n;i++)scanf("%d",&b[i].y),sy+=b[i].y;
	for(int i=1;i<=n;i++)scanf("%d",&b[i].v);
	for(int i=1;i<=n;i++){
		if(b[i].x<X)v1.push_back(b[i]);
		if(b[i].x>X)v2.push_back(b[i]);
	}
	v1.push_back(node(X,0,0)),v2.push_back(node(X,0,0));
	sort(v1.begin(),v1.end(),cmp2);
	sort(v2.begin(),v2.end(),cmp1);
	for(int i=1;i<v1.size();i++)s1[i]=s1[i-1]+v1[i].v;
	for(int i=1;i<v2.size();i++)s2[i]=s2[i-1]+v2[i].v;
//	for(int i=0;i<v1.size();i++)printf("%d %d %d\n",v1[i].x,v1[i].y,v1[i].v);puts("");
//	for(int i=0;i<v2.size();i++)printf("%d %d %d\n",v2[i].x,v2[i].y,v2[i].v);puts("");
	sv=s1[v1.size()-1]+s2[v2.size()-1];
	f[0][0][0]=f[0][0][1]=0;
	for(int i=0;i<v1.size();i++)for(int j=0;j<v2.size();j++){
		if(i)f[i][j][0]=f[i-1][j][0]+abs(v1[i].x-v1[i-1].x)*(sv-s1[i-1]-s2[j]);
		if(j)f[i][j][1]=f[i][j-1][1]+abs(v2[j].x-v2[j-1].x)*(sv-s1[i]-s2[j-1]);
		f[i][j][0]=min(f[i][j][0],f[i][j][1]+abs(v2[j].x-v1[i].x)*(sv-s1[i]-s2[j]));
		f[i][j][1]=min(f[i][j][1],f[i][j][0]+abs(v1[i].x-v2[j].x)*(sv-s1[i]-s2[j]));
	}
	double res=sy-min(f[v1.size()-1][v2.size()-1][0],f[v1.size()-1][v2.size()-1][1]);
	res/=1000;
	printf("%.3lf\n",res);
	return 0;
}

posted @ 2021-03-30 15:32  Troverld  阅读(40)  评论(0编辑  收藏  举报