【简●解】[SDOI2008] Sue的小球

【简●解】[SDOI2008] Sue的小球

计划着刷\(DP\)题结果碰到了这样一道论文题,幸好不是太难


【题目大意】

口水话有点多,所以就直接放链接。传送门

【分析】

看到题首先联想到了曾经做过的关路灯。所以先按\(x\)值排序,然后进行区间\(DP\)

不妨设\(f_1[i][j]\)\(f_2[i][j]\)分别表示从起点出发已射落\(i\)\(j\)这一段彩蛋,当前停留在\(i\)点,\(j\)点的最大得分\(v\)

考虑 \(f_1[i][j]\),即点\(i\)是当前射击的彩蛋,射击的得分与当前时刻挂钩,但 是当前的时刻是不能从\(f_1[i][j]\)的状态中表示出来的,我们进一步考虑 \(f_1[i][j]\)的求解。

由于射击\(i\)的得分是\(y_i−t∗v_i\),而\(t\)等于之前每一步决策移动的时间总和,这样我们就可以把\(t∗v_i\)​在之前的移动中就计算,也就是说每次移动都要把未来会减少的得分计算在内。 比如说从\(f_1[i+1][j]\)推到\(f_1[i][j]\),即从\(i+1\)走到\(i\)时除了\(i+1\)\(j\)这一段彩蛋外,其它的彩蛋都在下落,将这丢失的分数一并计算到从\(i+1\)走到\(i\)中。由于\(-t*v_i\)已经在之前决策时计算,所以射击时直接加上\(y_i\)即可。

所以可以先用\(sum[]\)计算\(v_i\)的前缀和,然后\(DP\)方程:
\[ f_1[i][j]=y[i]+max(f_1[i+1][j]+Sum(i+1,j)*(x_{i+1}-x_i),f_2[i+1][j]+Sum(i+1,j)*(x_j-x_i) \]

\[ f_2[i][j]=y[i]+max(f_1[i][j-1]+Sum(i,j-1)*(x_{j}-x_{i}),f_2[i][j-1]+Sum(i,j-1)*(x_j-x_{j-1}) \]

然后\(O(n^2)\)过。

【Code】

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#define ll long long
using namespace std;
const int MAX = 1000 + 5; 
const int INF = 0x3f3f3f3f;
inline int read(){
    int f = 1, x = 0;char ch;
    do { ch = getchar(); if (ch == '-') f = -1; } while (ch < '0'||ch>'9');
    do {x = x*10+ch-'0'; ch = getchar(); } while (ch >= '0' && ch <= '9'); 
    return f*x;
}
int n, bj;
double x0, f[3][MAX][MAX], sum[MAX];
struct sakura { double x, y, v; }sak[MAX];
inline bool cmp(sakura a, sakura b) { return a.x < b.x; }
inline double Sum(int l, int r) { return sum[n] - sum[r] + sum[l - 1]; }
inline double ab(double a) { return a < 0 ? -a : a; }
int main(){
    n = read(); ++n, x0 = read(); 
    sak[1].x = x0, sak[1].y = sak[1].v = 0;
    for (int i = 2;i <= n; ++i) {
        sak[i].x = read();
    }
    for (int  i = 2;i <= n; ++i) {
        sak[i].y = read();
    }
    for (int i = 2;i <= n; ++i) {
        sak[i].v = read(); 
    }
    sort(sak + 1, sak + 1 + n, cmp);
    for (int i = 1;i <= n; ++i) {
        sum[i] = sum[i - 1] + sak[i].v;
        if (ab(sak[i].x - x0) <= 1e-10 && ab(sak[i].y) <= 1e-10) {
            bj = i;
        }
    }
    memset(f, -INF, sizeof (f));
    f[1][bj][bj] = f[2][bj][bj] = 0.0;
    for (int k = 1;k <= n; ++k) {
        for (int i = 1;i + k <= n; ++i) {
            int j = i + k;
            f[1][i][j] = sak[i].y + max(f[1][i + 1][j] - (sak[i + 1].x - sak[i].x) * Sum(i + 1, j), f[2][i + 1][j] - (sak[j].x - sak[i].x) * Sum(i + 1, j));
            f[2][i][j] = sak[j].y + max(f[1][i][j - 1] - (sak[j].x - sak[i].x) * Sum(i, j - 1), f[2][i][j -1] - (sak[j].x - sak[j - 1].x) * Sum(i, j - 1)); 
        }
    }
    printf("%.3lf", max(f[1][1][n], f[2][1][n]) / 1000.0);
    return 0;
}

后来听人说这是未来\(DP\)???

转载于:https://www.cnblogs.com/silentEAG/p/10935723.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值