洛谷 P2466 Sue的小球 解題報告
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\)
第二行為\(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\)號彩蛋時的區間所產生的最大分數。
試試寫轉移的話,我們會發現兩個問題。
對於當前位置的信息我們需要存儲一下,也就是在左端點還是右端點,多開一維維護即可。
對於彩蛋的分數,是和時間掛鉤的,如果想完整的求解,得把時間壓進去啊。
時間壓進去肯定爆了有沒有別的辦法呢?
當然有,每一個點的最終得分其實不就等於 它的初始得分 減去 它的損失分 嗎
我們嘗試維護這樣一個損失分的最小值。
對於 每一段轉移的時候 我們都能求出 這段時間內 還沒有得到的彩蛋的分的 損失值
\(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的小球 解題報告