[SDOI2008] Sue 的小球 題解

2017BeiJiang發表於2024-06-02

題目描述

首先將彩蛋按照橫座標從小到大排序,依次標號為 \(1\sim n\)

顯然,\(Sue\)走過一段時間後,走過的點一定屬於一段連續區間。所以本題採用區間 \(dp\)

不妨先做一個簡單轉化,由於每個彩蛋初始高度確定,若想讓總分最高,就要使扣分最少。所以下面的 \(dp\) 從扣分最少入手。

\(f_{l,r,0/1}\) 表示走過 \(l\sim r\) 所有點,現在處於 \(l/r\),扣的最少分。

轉移的關鍵就是處理時間。

其中一種顯然的想法是,如果能處理當前的時間,那麼由 \(A\)(當前點) 點走到 \(B\) 點,就能計算出到達 \(B\) 點的時間,並相應扣除分值,但是這樣不得不在狀態中加多一維時間,即 \(f_{l,r,t,0/1}\),空間爆炸,不行

剛剛的想法將所有點獨立,即到達一個點在計算貢獻,不妨將所有點看做整體。具體而言,從 \(A\) 點到 \(B\) 點需要時間 \(t\),計算沒經過的點在時間 \(t\) 內損失的分值。設沒經過的點速度總值為 \(sv\),那麼損失分支為 \(sv\times t\)

由於這種方法中,兩點之間時間可計算,經過哪些點從狀態 \(l,r\) 可知,速度總值字首和計算即可。

剩下轉移看程式碼:

#include<bits/stdc++.h>
using namespace std;

typedef long long LL;
const int N=1100;
int n,st;
LL f[N][N][2],sv[N];
struct node {
    int x,y,v;
}a[N];

int cmp(node x,node y) {
    return x.x<y.x;
}

LL calc(int l,int r) {
    return sv[r]-sv[l-1];
}

int main(){
    cin>>n>>st;
    for(int i=1;i<=n;i++) cin>>a[i].x;
    for(int i=1;i<=n;i++) cin>>a[i].y;
    for(int i=1;i<=n;i++) cin>>a[i].v;
    sort(a+1,a+1+n,cmp);
    for(int i=1;i<=n;i++) {
        sv[i]=sv[i-1]+a[i].v;
    }
    LL vsum=sv[n];
    memset(f,0x3f,sizeof(f));
    //損失的最小值
    for(int i=1;i<=n;i++) {
        f[i][i][0]=f[i][i][1]=1ll*abs(st-a[i].x)*vsum;
    }

    // for(int i=1;i<=n;i++) cout<<f[i][i][0]<<" ";

    for(int len=2;len<=n;len++) {
        for(int l=1;l+len-1<=n;l++) {
            int r=l+len-1;
            f[l][r][0]=min({f[l][r][0],f[l+1][r][0]+1ll*(a[l+1].x-a[l].x)*(vsum-calc(l+1,r)),f[l+1][r][1]+1ll*(a[r].x-a[l].x)*(vsum-calc(l+1,r))});
            f[l][r][1]=min({f[l][r][1],f[l][r-1][1]+1ll*(a[r].x-a[r-1].x)*(vsum-calc(l,r-1)),f[l][r-1][0]+1ll*(a[r].x-a[l].x)*(vsum-calc(l,r-1))});
            f[l][r][0]=min(f[l][r][0],f[l][r][1]+1ll*(a[r].x-a[l].x)*(vsum-calc(l,r)));
            f[l][r][1]=min(f[l][r][1],f[l][r][0]+1ll*(a[r].x-a[l].x)*(vsum-calc(l,r)));

            // cout<<"DEBUG:"<<"["<<l<<" "<<r<<"]:"<<f[l][r][0]<<" "<<f[l][r][1]<<endl;
        }
    }
    LL ysum=0;
    for(int i=1;i<=n;i++) ysum+=a[i].y;
    LL ans=ysum-min(f[1][n][0],f[1][n][1]);
    printf("%.3lf",ans/1000.0);
    return 0;
}
/*
*/

相關文章