洛谷P1220 关路灯
written on 2022-02-06
关于区间dp的话,其实自己还懂得不是特别多
所以第一篇luogu博客就交给 关路灯 这道练习区间dp和dp转移的好题好了
首先审题,对于一道可能作为动态规划的题目来说,我们应该先去关注的是:一个状态是从何而来的
我们发现,这个人来回走动去关路灯,每一个阶段肯定是多关了一个路灯,然而又是在区间内运动并且转移的,那么这就是一道经典而好做的与实际问题结合的区间dp题
首先定义状态\(f[l][r]\)为关掉所有在区间\(l,r\)内的路灯 (注意,其它区间的灯都没有关掉) 所花费的最少功率
但是还要确定人的位置,于是再在最后加一维表示人在l还是在r (因为关完这一区间的路灯后人肯定是在l或r的) 即\(f[l][r][k],k==0\) 或 \(1\)
消耗的功率,自然就要看其它不在这一区间内的路灯了。这里我们可以用一个前缀和来维护总功率
于是,状态转移就显然了,可以看看代码里的转移方程,再自己理解一下
#include<bits/stdc++.h>
typedef long long ll;
using namespace std;
int n,st;
ll f[205][205][2];
ll p[205],a[205],s[205];
int main()
{
scanf("%d%d",&n,&st);
memset(f,0x7f,sizeof(f));
for(int i=1;i<=n;i++) scanf("%lld%lld",&p[i],&a[i]),s[i]=s[i-1]+a[i];
f[st][st][0]=f[st][st][1]=0;
for(int len=2;len<=n;len++)
{
for(int i=1;i<=n-len+1;i++)
{
int j=i+len-1;
f[i][j][0]=min(f[i][j][0],f[i+1][j][0]+(s[i]+s[n]-s[j])*(p[i+1]-p[i]));
f[i][j][0]=min(f[i][j][0],f[i+1][j][1]+(s[i]+s[n]-s[j])*(p[j]-p[i]));
f[i][j][1]=min(f[i][j][1],f[i][j-1][1]+(s[i-1]+s[n]-s[j-1])*(p[j]-p[j-1]));
f[i][j][1]=min(f[i][j][1],f[i][j-1][0]+(s[i-1]+s[n]-s[j-1])*(p[j]-p[i]));
}
}
printf("%lld",min(f[1][n][1],f[1][n][0]));
return 0;
}