1. 程式人生 > >洛谷 P2466 Sue的小球 解題報告

洛谷 P2466 Sue的小球 解題報告

space 小球 維護 friend ons 描述 () 區間 都是

P2466 [SDOI2008]Sue的小球

題目描述

Sue和Sandy最近迷上了一個電腦遊戲,這個遊戲的故事發在美麗神秘並且充滿刺激的大海上,Sue有一支輕便小巧的小船。然而,Sue的目標並不是當一個海盜,而是要收集空中漂浮的彩蛋,Sue有一個秘密武器,只要她將小船劃到一個彩蛋的正下方,然後使用秘密武器便可以在瞬間收集到這個彩蛋。然而,彩蛋有一個魅力值,這個魅力值會隨著彩蛋在空中降落的時間而降低,Sue要想得到更多的分數,必須盡量在魅力值高的時候收集這個彩蛋,而如果一個彩蛋掉入海中,它的魅力值將會變成一個負數,但這並不影響Sue的興趣,因為每一個彩蛋都是不同的,Sue希望收集到所有的彩蛋。

然而Sandy就沒有Sue那麽浪漫了,Sandy希望得到盡可能多的分數,為了解決這個問題,他先將這個遊戲抽象成了如下模型:

以Sue的初始位置所在水平面作為\(x\)軸。

一開始空中有\(N\)個彩蛋,對於第\(i\)個彩蛋,他的初始位置用整數坐標\((xi, yi)\)表示,遊戲開始後,它勻速沿\(y\)軸負方向下落,速度為\(v_i\)單位距離/單位時間。Sue的初始位置為\((x0, 0)\),Sue可以沿x軸的正方向或負方向移動,Sue的移動速度是1單位距離/單位時間,使用秘密武器得到一個彩蛋是瞬間的,得分為當前彩蛋的\(y\)坐標的千分之一。

現在,Sue和Sandy請你來幫忙,為了滿足Sue和Sandy各自的目標,你決定在收集到所有彩蛋的基礎上,得到的分數最高。

輸入輸出格式

 輸入格式:

第一行為兩個整數\(N\)

, \(x0\)用一個空格分隔,表示彩蛋個數與Sue的初始位置。

第二行為\(N\)個整數\(x_i\),每兩個數用一個空格分隔,第\(i\)個數表示第\(i\)個彩蛋的初始橫坐標。

第三行為\(N\)個整數\(y_i\),每兩個數用一個空格分隔,第\(i\)個數表示第\(i\)個彩蛋的初始縱坐標。

第四行為\(N\)個整數\(v_i\),每兩個數用一個空格分隔,第\(i\)個數表示第\(i\)個彩蛋勻速沿\(y\)軸負方向下落的的速度。

輸出格式:

一個實數,保留三位小數,為收集所有彩蛋的基礎上,可以得到最高的分數。

說明

對於30%的數據,\(N<=20\)

對於60%的數據,\(N<=100\)

對於100%的數據,\(-10^4 <= xi,yi,vi <= 10^4,N < = 1000\)


從暴力開始

我們發現,對於當前時間所處的每一個位置,我們有且僅有兩個選擇(即向左或向右)

30分到手,\(O(2^N)\)的復雜度。

既然有向左或向右的選擇,很容易聯想到DP上去啊。

\(dp[i][j]\)代表已經處理左端為\(i\)號彩蛋和右端為\(j\)號彩蛋時的區間所產生的最大分數。

試試寫轉移的話,我們會發現兩個問題。

  1. 對於當前位置的信息我們需要存儲一下,也就是在左端點還是右端點,多開一維維護即可。

  2. 對於彩蛋的分數,是和時間掛鉤的,如果想完整的求解,得把時間壓進去啊。

時間壓進去肯定爆了有沒有別的辦法呢?

當然有,每一個點的最終得分其實不就等於 它的初始得分 減去 它的損失分 嗎

我們嘗試維護這樣一個損失分的最小值。

對於 每一段轉移的時候 我們都能求出 這段時間內 還沒有得到的彩蛋的分的 損失值

\(dp[i][j][0]=min(dp[i+1][j][0]+(t[i+1].x-t[i].x)*(sumv[i]+sumv[n]-sumv[j]),\)
\(dp[i+1][j][1]+(t[j].x-t[i].x)*(sumv[i]+sumv[n]-sumv[j]));\)
\(dp[i][j][1]=min(dp[i][j-1][0]+(t[j].x-t[i].x)*(sumv[i-1]+sumv[n]-sumv[j-1]),\)
\(dp[i][j-1][1]+(t[j].x-t[j-1].x)*(sumv[i-1]+sumv[n]-sumv[j-1]));\)

其中,\(sumv[i]\)維護的是速度的前綴和數組。


code:

#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
const int N=1002;
double x0,sumv[N],sumy;
int n;
double dp[N][N][2];
double abs(double x) {return x>0?x:-x;}
struct node
{
    double x,y,v;
    bool friend operator <(node n1,node n2)
    {
        return n1.x<n2.x;
    }
}t[N];
int main()
{
    scanf("%d%lf",&n,&x0);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            dp[i][j][0]=dp[i][j][1]=1e300;
    for(int i=1;i<=n;i++)
        scanf("%lf",&t[i].x);
    for(int i=1;i<=n;i++)
    {
        scanf("%lf",&t[i].y);
        sumy+=t[i].y;
    }
    for(int i=1;i<=n;i++)
        scanf("%lf",&t[i].v);
    sort(t+1,t+1+n);
    for(int i=1;i<=n;i++)
        sumv[i]=sumv[i-1]+t[i].v;
    for(int i=1;i<=n;i++)
        dp[i][i][0]=dp[i][i][1]=(abs(t[i].x-x0))*sumv[n];
    for(int i=n-1;i>=1;i--)
        for(int j=i+1;j<=n;j++)
        {
            dp[i][j][0]=min(dp[i+1][j][0]+(t[i+1].x-t[i].x)*(sumv[i]+sumv[n]-sumv[j]),
                            dp[i+1][j][1]+(t[j].x-t[i].x)*(sumv[i]+sumv[n]-sumv[j]));
            dp[i][j][1]=min(dp[i][j-1][0]+(t[j].x-t[i].x)*(sumv[i-1]+sumv[n]-sumv[j-1]),
                            dp[i][j-1][1]+(t[j].x-t[j-1].x)*(sumv[i-1]+sumv[n]-sumv[j-1]));
        }
    printf("%.3lf\n",(sumy-min(dp[1][n][0],dp[1][n][1]))/1000.0);
    return 0;
}

我居然又在離散化(排序)之前求了\(sumv\),還找了許久的錯啊。。。

丟人。。


2018.5.21

洛谷 P2466 Sue的小球 解題報告