题意:在一个一维坐标轴上有n盏灯,每盏灯有自己的坐标和功耗(单位:m和w)。有一个人,其初始位置为c(保证c有灯),他首先关掉初始位置的灯,然后向左或向右去关掉所有的灯(速度:1m/s,关灯不花费时间),求最后关掉所有灯花费的最小总功耗(单位:j)。
思路:区间dp,开三维dp[i][j][k]表示区间(i,j)内的灯全部关闭的最小花费(区间外的灯都是开的),k=0时表示人在这个区间的左端点,k=1则在右端点。
状态的转移:
对于在左端点的状态dp[i][j][0]可以由子区间(i+1,j)转移。从区间(i+1,j)扩张到区间(i,j)可以通过左端点i向左走一个灯的距离或者右端点j向左走j-i个灯的距离。最优状态在两个端点肯定可以取到,不用考虑中间的位置。
转移方程:dp[i][j][0]=min(dp[i+1][j][0] + 功耗花费1,dp[i+1][j][1] + 功耗花费2)
功耗花费 = 走路时间*开着的灯的总功率
走路时间直接相减就可以得到;开着的灯的总功率可以用前缀和来快速计算,所有灯的总功率 - (i+1,j)区间内的功率和如:w[n] - (w[j] - w[i+1-1])
对于在右端点的状态同理。
初始状态:先全部初始化为一个极大值,对于初始位置c,该点的功耗是0,dp[c][c][0]=dp[c][c][1]=0
参考代码:
#include<bits/stdc++.h>
using namespace std;
const int inf=1e9+7;
int t[51];
int sum[51];
int dp[55][55][2];
int main()
{
int n,m;
scanf("%d%d",&n,&m);
for(int i=1,w;i<=n;i++)
{
scanf("%d%d",&t[i],&w);
sum[i]=sum[i-1]+w;
}
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
dp[i][j][0]=dp[i][j][1]=inf;
dp[m][m][0]=dp[m][m][1]=0;
for(int len=2;len<=n;len++)
{
for(int i=1,j=i+len-1;j<=n;i++,j++)
{
dp[i][j][0]=min(dp[i+1][j][0]+(t[i+1]-t[i])*(sum[n]-(sum[j]-sum[i])),dp[i+1][j][1]+(t[j]-t[i])*(sum[n]-(sum[j]-sum[i])));
dp[i][j][1]=min(dp[i][j-1][1]+(t[j]-t[j-1])*(sum[n]-(sum[j-1]-sum[i-1])),dp[i][j-1][0]+(t[j]-t[i])*(sum[n]-(sum[j-1]-sum[i-1])));
}
}
printf("%d\n",min(dp[1][n][0],dp[1][n][1]));
return 0;
}