小技巧之费用前移

小技巧之费用前移

有些时候序列\(dp\)看似是有后效性的,但是换一种角度考虑,算出这个点\(i(1<=i<=n)\)对所有点\(n\)的贡献也可。

如, 例题:关路灯

题意简介:

老张可以每次向左或向右走去关路灯,每盏路灯对答案的贡献为功率乘被关掉的时间.

解法:

我们设\(f[l][r][0/1]\)表示关完\([l,r]\)区间,最后走到左/右端点后花费的最小能量,

但如果我们算每个点单独的贡献,则需要知道走到这个点的时间,

但是没法记录(不满足最优性啊)(我反正是不知道)

怎么办,我们直接算这一段对所有点的贡献,则这一段的最小能量=上一段的最小能量+上一段走到这一段的距离*(除这一段已关的路灯为所有路灯的功率和);

代码:

#include<bits/stdc++.h>
using namespace std;
const int N=106;
int n,c,r,f[N][N][2],w[N],s[N],sum[N];
inline int read(){
   int T=0,F=1; char ch=getchar();
   while(ch<'0'||ch>'9'){if(ch=='-') F=-1; ch=getchar();}
   while(ch>='0'&&ch<='9') T=(T<<3)+(T<<1)+(ch-48),ch=getchar();
   return F*T;  
}
int main(){
    n=read(),c=read(),memset(f,0x3f,sizeof(f));
    for(int i=1;i<=n;++i) s[i]=read(),w[i]=read(),sum[i]=sum[i-1]+w[i];
    f[c][c][0]=f[c][c][1]=0;
    for(int len=2;len<=n;++len)
        for(int l=1;l<=n-len+1;++l){
            r=l+len-1;
            f[l][r][0]=min(min(f[l][r-1][0]+(s[r]-s[l])*(sum[n]-sum[r-1]+sum[l-1]),f[l][r-1][1]+(s[r]-s[r-1])*(sum[n]-sum[r-1]+sum[l-1]))+(s[r]-s[l])*(sum[n]-sum[r]+sum[l-1]),min(f[l+1][r][0]+(s[l+1]-s[l])*(sum[n]-sum[r]+sum[l]),f[l+1][r][1]+(s[r]-s[l])*(sum[n]-sum[r]+sum[l])));
            f[l][r][1]=min(min(f[l][r-1][0]+(s[r]-s[l])*(sum[n]-sum[r-1]+sum[l-1]),f[l][r-1][1]+(s[r]-s[r-1])*(sum[n]-sum[r-1]+sum[l-1])),min(f[l+1][r][0]+(s[l+1]-s[l])*(sum[n]-sum[r]+sum[l]),f[l+1][r][1]+(s[r]-s[l])*(sum[n]-sum[r]+sum[l]))+(s[r]-s[l])*(sum[n]-sum[r-1]+sum[l-1]));
        }
    printf("%d\n",min(f[1][n][0],f[1][n][1]));
    return 0;
}
posted @ 2019-08-21 22:08  lsoi_ljk123  阅读(127)  评论(0编辑  收藏  举报