题目链接:关路灯 - 洛谷
分析:这道题是一道比较好的区间DP题,首先从状态表示上来说,仅仅表示哪些灯亮着是不行的,还需要表示出当前所在的位置,一开始我以为这是一道状压DP,但是看了一眼数据范围发现用状压来解决肯定TLE,我们需要先想明白一个问题,就是有没有一种可能是在动态转移过程中最优答案的顺序包含一种i和j灯都已经被关掉但是i和j中间的灯还有没被关掉的?仔细想想发现这种情况是不可能存在的对吧,因为i和j灯都被关掉说明i和j之间的所有灯都被经过了,关灯又不需要时间,我们为什么不直接关掉呢?这也是这道题为什么可以用区间DP解决的原因。所以可以得到的一个结论是,在动态转移过程中被关掉的灯一定是连续的而不可能是间断的,所以我们就可以令f[i][j][0/1]表示i~j的灯已关闭且当前位置在i/j的时候最少的功耗,然后我们就可以进行动态转移了
下面我们来思考一下如何计算每次的功耗,这个比较简单就是(总的功率-已经灭掉的功率)*当前动态转移所需时间即可,我们可以令sum[i]表示前i个灯的功耗是多少,这样我们就可以o(1)求出连续区间的功耗,这样的话本道题就只剩下动态转移方程了,下面进行动态转移方程的讲解:
对于f[i][j][0]来说,我们枚举中间点k,这个肯定是由f[k][j][0/1]来更新的,因为f[i][j][0]表示最终位置在i,说明i这个位置的灯还没被关掉,所以不可能是由f[i][k][0/1]来进行更新,具体更新也比较简单,比如f[k][j][0]表示当前在k点,要向i点走,总地时间就是a[k]-a[i],这段时间里亮着的灯的功率为sum[n]-(sum[j]-sum[k-1]),因为k~j的灯都已经被关灭,所以动态转移方程就是这样的了,其他的状态也是一样的,我就不一个一个分析了,下面是动态转移方程代码:
f[i][j][0]=min(f[i][j][0],f[k][j][0]+(a[k]-a[i])*(sum[n]-(sum[j]-sum[k-1])));//当前位置在k且向i走
f[i][j][0]=min(f[i][j][0],f[k][j][1]+(a[j]-a[i])*(sum[n]-(sum[j]-sum[k-1])));//当前位置在j且向i走
f[i][j][1]=min(f[i][j][1],f[i][k][0]+(a[j]-a[i])*(sum[n]-(sum[k]-sum[i-1])));//当前位置在i且向j走
f[i][j][1]=min(f[i][j][1],f[i][k][1]+(a[j]-a[k])*(sum[n]-(sum[k]-sum[i-1])));//当前位置在k且向j走
至于初始化就比较简单了,一开始在的位置是pos,那么直接令f[pos][pos][0]=f[pos][pos][1]=0即可
下面是完整代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<queue>
using namespace std;
typedef long long ll;
const int N=53;
ll f[N][N][2];//f[i][j][0/1]表示i~j的灯已关闭且当前位置在i/j的时候最少的功耗
ll a[N],sum[N];//sum[i]表示前i个灯1s的功耗
int main()
{
int n,pos;
cin>>n>>pos;
memset(f,0x3f,sizeof f);
f[pos][pos][0]=f[pos][pos][1]=0;
for(int i=1;i<=n;i++)
scanf("%lld%lld",&a[i],&sum[i]),sum[i]+=sum[i-1];
for(int len=2;len<=n;len++)
for(int i=1;i+len-1<=n;i++)
{
int j=i+len-1;
for(int k=i;k<=j;k++)
{
f[i][j][0]=min(f[i][j][0],f[k][j][0]+(a[k]-a[i])*(sum[n]-(sum[j]-sum[k-1])));//当前位置在k且向i走
f[i][j][0]=min(f[i][j][0],f[k][j][1]+(a[j]-a[i])*(sum[n]-(sum[j]-sum[k-1])));//当前位置在j且向i走
f[i][j][1]=min(f[i][j][1],f[i][k][0]+(a[j]-a[i])*(sum[n]-(sum[k]-sum[i-1])));//当前位置在i且向j走
f[i][j][1]=min(f[i][j][1],f[i][k][1]+(a[j]-a[k])*(sum[n]-(sum[k]-sum[i-1])));//当前位置在k且向j走
}
}
printf("%lld",min(f[1][n][0],f[1][n][1]));
return 0;
}
|