luogu P1220 关路灯
原题链接:https://www.luogu.org/problemnew/show/1220
做过的唯一一道区间DP。
首先说明题目中的贪心为什么是错的。由初中物理可知,消耗的电能是功率与时间的乘积,在此题中,所有没有被关掉的路灯每一秒都会耗电,不能单纯地用功率来考虑问题。
发现贪心不成立,考虑区间DP,f[i][j][0/1]表示已经关闭了[i,j]的区间的路灯,当前在左/右侧。
为了计算还没有关掉的路灯的耗能总和,使用前缀和。
每次转移的时候,都有当前区间向左或是向右拓展的方案,而这期间消耗的能量就是区间之外所有路灯的功率之和与行走时间的乘积。
#include<cstdio> void read(int &y) { y=0;char x=getchar(); while(x<'0'||x>'9') x=getchar(); while(x>='0'&&x<='9') { y=y*10+x-'0'; x=getchar(); } } int min(int x,int y) { if(x<y) return x; else return y; } int abs(int x) { if(x<=0) x*=-1; return x; } int n,c,x[55],w[55],f[55][55][2]; int sum[55]; int main() { read(n);read(c); for(int i=1;i<=n;i++) { read(x[i]); read(w[i]); sum[i]=sum[i-1]+w[i]; } for(int i=1;i<=n;i++) f[i][i][0]=f[i][i][1]=sum[n]*abs(x[i]-x[c]); 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+1][j][0]+(x[i+1]-x[i])*(sum[n]-sum[j]+sum[i]),f[i+1][j][1]+(x[j]-x[i])*(sum[n]-sum[j]+sum[i])); f[i][j][1]=min(f[i][j-1][1]+(x[j]-x[j-1])*(sum[n]-sum[j-1]+sum[i-1]),f[i][j-1][0]+(x[j]-x[i])*(sum[n]-sum[j-1]+sum[i-1])); } } printf("%d",min(f[1][n][0],f[1][n][1])); return 0; }