[题解]luogu_P2466(区间dp

https://www.luogu.org/blog/Bartholomew/solution-p2466

首先看着像关路灯,$f[i][j][0/1]$表示关完$i~j$正在$i/j$,但是我们不知道花费的时间,没法转移

其实花费的时间是和之前的决策有关的,$yi-vi*t$,我们必须在之前花费时间时就把$t*vi$计算在内,

也就是所有价值的总和是固定的,我们移动会造成价值的损失,现在要求这个最小损失,每次移动造成的损失可以用前缀和维护下落速度

转移和关路灯一样

核心:

引自论文: 在常规动态规划问题中,我们面临当前状态时“行动”造成的花费往往与这个状态是同时计算的。 例如在经典的石子合并问题中,规划方程为
$f[i][j]=max{f[i][k]+f[k+1][j]}+w(i,j)$
当我们计算 f[i][j]时,才会把将 i 到 j 的石子全部合并到一起这一“行动”的费用加进去。这很符合我们的思维习惯。 然而近年来频繁出现一类动态规划问题,在这类问题中,当前“行动”的费用的一部分需要在之前决策时被计算并以状态的形式对当前状态造成影响。造成这一独特的计算的原因就是当前的决策会对未来的“行动”费用造成影响。这类问题构造方程往往比较困难,需要仔细分析原题,找到矛盾所在。

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=1009;
int n,x0;
struct node{
    int x,y,v;
    bool operator <(const node&t)const{
        return x<t.x;
    }
}p[maxn];
int s[maxn],sum;
int f[maxn][maxn][2];
int main(){
    scanf("%d%d",&n,&x0);
    for(int i=1;i<=n;i++)scanf("%d",&p[i].x);
    for(int i=1;i<=n;i++)scanf("%d",&p[i].y),sum+=p[i].y;
    for(int i=1;i<=n;i++)scanf("%d",&p[i].v);
    p[++n].x=x0;
    sort(p+1,p+1+n);
    for(int i=1;i<=n;i++)s[i]=s[i-1]+p[i].v;
    memset(f,0x3f,sizeof(f));
    for(int i=1;i<=n;i++)if(p[i].v==0&&p[i].x==x0)f[i][i][0]=f[i][i][1]=0;
    for(int l=2;l<=n;l++)
    for(int i=1;i<=n-l+1;i++){
        int j=i+l-1;
        f[i][j][0]=min(f[i][j][0],f[i+1][j][0]+(p[i+1].x-p[i].x)*((s[i]-s[0])+(s[n]-s[j])));
        f[i][j][0]=min(f[i][j][0],f[i+1][j][1]+(p[j].x-p[i].x)*((s[i]-s[0])+(s[n]-s[j])));
        f[i][j][1]=min(f[i][j][1],f[i][j-1][1]+(p[j].x-p[j-1].x)*((s[i-1]-s[0])+(s[n]-s[j-1])));
        f[i][j][1]=min(f[i][j][1],f[i][j-1][0]+(p[j].x-p[i].x)*((s[i-1]-s[0])+(s[n]-s[j-1])));    
    }
    printf("%.3lf\n",(1.0*sum-min(f[1][n][0],f[1][n][1]))/1000.0);
}

 

posted @ 2019-10-25 20:16  羊肉汤泡煎饼  阅读(203)  评论(0编辑  收藏  举报