luogu P1220 关路灯
题意
给你每盏路灯的位置与单位时间内的耗电量,以及你的出发位置以及单位时间内移动的距离,求在你关闭所有的路灯之前,最小的耗电总量。(一个路灯被关闭后就不再耗电)
解法
这道题与lrj的算法入门经典P293的那一道题差不多,通过分析我们知道,在任意时刻,已经关掉的灯构成一个区间,并且人一定在这个区间的左端点或者右端点(因为如果你为了关掉一个灯而路过了另一个灯,那么你一定会将路过的灯都关掉)。于是我们设 (dp[i][j][0]) 表示现在已经关掉灯的区间为 ([i,j]) 且人在左端点, (dp[i][j][1]) 表示现在已经关掉灯的区间为 ([i,j]) 且人在右端点。于是有转移方程:
$ dp[i][j][0] = min(dp[i+1][j][0] + cost(i,i+1) , dp[i+1][j][1] + cost(j,i)) $
$ dp[i][j][1] = min(dp[i][j-1][0] + cost(i,j) , dp[i][j-1][1] + cost(j,j-1)) $
其中 $ cost(i,j) $ 为人从 i 走到 j 的时间中其它未关闭的灯的耗电量。
代码
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <cctype>
#define INF 2139062143
#define MAX 0x7ffffffffffffff
#define del(a,b) memset(a,b,sizeof(a))
using namespace std;
typedef long long ll;
template<typename T>
inline void read(T&x)
{
x=0;T k=1;char c=getchar();
while(!isdigit(c)){if(c=='-')k=-1;c=getchar();}
while(isdigit(c)){x=x*10+c-'0';c=getchar();}x*=k;
}
const int maxn=50+5;
int dp[maxn][maxn][2];
int pos[maxn];
int sum[maxn];
int n,c;
int main()
{
read(n),read(c);
for(int i=1,w;i<=n;i++) {
read(pos[i]),read(w);
sum[i]=sum[i-1]+w;
}
for(int i=1;i<=n;i++) dp[i][i][0]=dp[i][i][1]=sum[n]*abs(pos[i]-pos[c]);//因为只熄灭了一个灯,所以所有的灯都燃烧了那么久。
for(int len=2;len<=n;len++)
for(int i=1;i+len-1<=n;i++){
int j=i+len-1;
dp[i][j][0]=min(
dp[i+1][j][0]+abs(pos[i+1]-pos[i])*(sum[i]+sum[n]-sum[j]),
dp[i+1][j][1]+abs(pos[j]-pos[i])*(sum[i]+sum[n]-sum[j]));
dp[i][j][1]=min(
dp[i][j-1][1]+abs(pos[j-1]-pos[j])*(sum[i-1]+sum[n]-sum[j-1]),
dp[i][j-1][0]+abs(pos[i]-pos[j])*(sum[i-1]+sum[n]-sum[j-1]));
}
printf("%d",min(dp[1][n][0],dp[1][n][1]));
return 0;
}