小技巧之费用前移
小技巧之费用前移
有些时候序列\(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;
}