[SDOI2008]Sue的小球 题解
Sue的小球 题解
题意
\(~~~~\) 给了 \(n\) 个小球,第 \(i\) 个小球在 \(x_i\) 初始分值为 \(y_i\) ,同时它会以 \(\dfrac{v_i}{1000}/\texttt{单位时间}\) 的速度降低。(可以降低为负数)从初始位置开始,每个单位时间可以移动 \(1\) 单位长度。收集小球不需要时间,求收集所有小球后能获得的最大分值。
题解
\(~~~~\) 首先,可以想到对每个球按位置从小到大排序。由于总的得分固定,我们可以求最小损失,然后考虑:对于两个小球 \(l,r (x_l<x_r)\) ,若它们都被收集了,则小球 \(x\in[l,r]\) 也被收集一定是最优方案:
\(~~~~\) 证明:显然。 每个小球被收集,一定是从起始点走到过该小球的,则此时顺手收集路上的小球一定不会使答案变劣,因为收集不需要时间。
\(~~~~\) 所以,我们考虑已经收集了 \([l,r]\) 的小球时,下一步一定是扩展到 \(l-1\) 或 \(r+1\) ,不难想到定义 \(\texttt{DP}\) 状态:令 \(dp_{i,j}\) 表示已经收集 \([l,r]\) 的所有小球时的最小损失。考虑 \(\texttt{DP}\) 转移时需要当前的位置,因此增加一维,用来记录最后停留在 \(i\) 或者 \(j\) (当然也可以定义两个数组)。
\(~~~~\) 发现还是有问题, \(\texttt{DP}\) 转移还需要记录当前的时间,可是如果我们把时间也加在 \(\texttt{DP}\) 维度上,时间就爆了。
\(~~~~\) 这时就轮到这道题的精髓了——提前计算。我们是可以算出每一次转移时的时间的,那如果我们把每次转移时其他小球损失的分数也加上去,既然每个小球都将会被收集,那么最后的效果其实是一样的。
\(~~~~\) 具体来说,如果有小球 \([l,r]\) 已经被收集,则下一次转移时 \([1,l)\) 与 \((r,n]\) 的所有会被消耗的分数都可以直接加在下一次。
\(~~~~\) 若设:\(pre_{i,j}\) 表示 \(\sum_{k=i}^j v_k\) ,则有 \(\texttt{DP}\) 转移方程。
\(~~~~\) 然后转移就好了。
\(~~~~\) 注意处理初始值时可以增加一个小球使得它的 \(x=x_0,y=0,v=0\) ,初始化时就令这个小球为 \(0\) 即可。
代码
#include <cstdio>
#include <cstring>
#include <algorithm>
#define ll long long
using namespace std;
template<typename T>void read(T &x)
{
T f=1;x=0;char s=getchar();
while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
while(s>='0'&&s<='9') {x=x*10+s-'0';s=getchar();}
x*=f;
}
struct node{
int x,y,v;
}b[1005];
inline bool cmp(node x,node y) {return x.x<y.x;}
ll dp1[1005][1005],dp2[1005][1005],w[1005];
inline int Abs(int x){return x>0?x:-x;}
int calc(int l,int r){return w[r]-w[l-1];}
int main() {
int n,x;
ll tot=0;
read(n);read(x);
for(int i=1;i<=n;i++) read(b[i].x);
for(int i=1;i<=n;i++) read(b[i].y),tot+=b[i].y;
for(int i=1;i<=n;i++) read(b[i].v);
b[++n].x=x;
sort(b+1,b+n+1,cmp);
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++) dp1[i][j]=dp2[i][j]=1e18;
}
for(int i=1;i<=n;i++)
{
w[i]=w[i-1]+b[i].v;
if(b[i].v==0&&b[i].x==x) dp1[i][i]=dp2[i][i]=0;
}
//dp1[i][j] 收集了第 i个 至 第 j个彩蛋 最后停留在 i
//dp2[i][j]··················· j
for(int len=2;len<=n;len++)
{
for(int i=1;i+len-1<=n;i++)
{
int j=i+len-1;
dp1[i][j]=min(dp1[i][j],dp1[i+1][j]+Abs(b[i+1].x-b[i].x)*(calc(1,i)+calc(j+1,n)));
dp1[i][j]=min(dp1[i][j],dp2[i+1][j]+Abs(b[ j ].x-b[i].x)*(calc(1,i)+calc(j+1,n)));
dp2[i][j]=min(dp2[i][j],dp1[i][j-1]+Abs(b[ i ].x-b[j].x)*(calc(1,i-1)+calc(j,n)));
dp2[i][j]=min(dp2[i][j],dp2[i][j-1]+Abs(b[j-1].x-b[j].x)*(calc(1,i-1)+calc(j,n)));
}
}
// printf("%d %d %d\n",tot-min(dp1[1][n],dp2[1][n]),dp1[1][n],dp2[1][n]);
printf("%.3lf",(tot-min(dp1[1][n],dp2[1][n]))/1000.0);
return 0;
}
\(~~~~\)