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;
}