SDOI2008 sue的小球
题目链接:戳我
区间DP,设\(dp[i][j][0/1]\)表示收集完\([i,j]\)这个区间的小球之后,现在在左端点(0)或者右端点(1),损失掉的最小收益是多少。
然后初始化只更新离初始点最近的两个点就行了qwq.
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define MAXN 1010
using namespace std;
int n;
double dp[MAXN][MAXN][2],sum[MAXN];
double all,x0;
struct Node{double x,y,k;}node[MAXN];
inline bool cmp(struct Node a,struct Node b){return a.x<b.x;}
int main()
{
#ifndef ONLINE_JUDGE
freopen("ce.in","r",stdin);
#endif
scanf("%d%lf",&n,&x0);
for(int i=1;i<=n;i++) scanf("%lf",&node[i].x);
for(int i=1;i<=n;i++) scanf("%lf",&node[i].y);
for(int i=1;i<=n;i++) scanf("%lf",&node[i].k);
sort(&node[1],&node[n+1],cmp);
for(int i=1;i<=n;i++) sum[i]=sum[i-1]+node[i].k;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
dp[i][j][0]=dp[i][j][1]=1e9;
bool flag=false;
for(int i=1;i<=n;i++)
{
if(node[i].x>=x0&&flag==false)
{
flag=true;
dp[i][i][0]=dp[i][i][1]=sum[n]*abs(x0-node[i].x);
dp[i-1][i-1][0]=dp[i-1][i-1][1]=sum[n]*abs(x0-node[i-1].x);
}
}
for(int i=n-1;i>=1;i--)
for(int j=i+1;j<=n;j++)
{
dp[i][j][0]=min(dp[i][j][0],dp[i+1][j][0]+(node[i+1].x-node[i].x)*(sum[i]+sum[n]-sum[j]));
dp[i][j][0]=min(dp[i][j][0],dp[i+1][j][1]+(node[j].x-node[i].x)*(sum[i]+sum[n]-sum[j]));
dp[i][j][0]=min(dp[i][j][0],dp[i][j-1][1]+(2*node[j].x-node[j-1].x-node[i].x)*(sum[i-1]+sum[n]-sum[j-1]));
dp[i][j][0]=min(dp[i][j][0],dp[i][j-1][0]+(2*node[j].x-2*node[i].x)*(sum[i-1]+sum[n]-sum[j-1]));
dp[i][j][1]=min(dp[i][j][1],dp[i][j-1][1]+(node[j].x-node[j-1].x)*(sum[i-1]+sum[n]-sum[j-1]));
dp[i][j][1]=min(dp[i][j][1],dp[i][j-1][0]+(node[j].x-node[i].x)*(sum[i-1]+sum[n]-sum[j-1]));
dp[i][j][1]=min(dp[i][j][1],dp[i+1][j][0]+(node[i+1].x-2*node[i].x+node[j].x)*(sum[i]+sum[n]-sum[j]));
dp[i][j][1]=min(dp[i][j][1],dp[i+1][j][1]+(2*node[j].x-2*node[i].x)*(sum[i]+sum[n]-sum[j]));
}
for(int i=1;i<=n;i++) all+=node[i].y;
printf("%.3lf\n",(all-min(1.0*dp[1][n][0],dp[1][n][1]))/1000.0);
return 0;
}