洛谷1220:关路灯
题意描述:
- 一条路线上安装了(n)盏路灯,每个灯有功率和位置。
- 关灯人每天早上需要关灯,他起始处于路灯(c)的位置处。
- 问最小消耗功率的关灯的方案的功率。
输入描述:
- 第一行输入一个(n)和(c)表示路灯数和关灯人起始位置。
- 接下来输入每盏灯的位置和功率。
数据范围:
- (1leq nleq 50,1leq cleq n)
思路:
-
区间(dp)。
-
关灯的话关灯的话一定是关掉一段区间的灯,其他的灯全部亮起。
-
那么我们可以设计(f(i,j))表示(i)到(j)这一段区间关灯的最小消耗。
-
那么初态就是(f(c,c)=0),其他的赋(INF),终态为(f(1,n))。
-
同时关灯过程有两种可能,第一种是接着关同一方向的灯,第二种是关反方向的灯。
-
而且每次关完一盏灯后,关灯人一定处于关了灯的一段区间的左端点或者右端点。
-
那么进一步为:(f(i,j,0/1)),分别表示关完区间(i,j)等时,关灯人处于左端点或者右端点的最小消耗。
-
有转移方程:
- (f(i,j,0)=min(f(i+1, j,0)+[p(i+1)-p(i)]*[sum(i)+(sum(n)-sum(j))],f(i+1,j,1)+[a(j)-a(i)]*[sum(i)+(sum(n)-sum(j))])
- 解释:关灯人关掉最左边那盏灯可以分别从:最左边关过来,或者原先在最右边再回头过来。
- (f(i,j,1)=min(f(i,j-1,0)+[a(j)-a(i)]*[sum(i-1)+(sum(n)-sum(j-1))],f(i,j-1,1)+[a(j)-a(j-1)]*[sum(i-1)+(sum(n)-sum(j-1))]))
- 解释:如上同理。
- 其中(sum)表示前缀和数组。
- (f(i,j,0)=min(f(i+1, j,0)+[p(i+1)-p(i)]*[sum(i)+(sum(n)-sum(j))],f(i+1,j,1)+[a(j)-a(i)]*[sum(i)+(sum(n)-sum(j))])
-
最后(ans=min(f(1,n,1),f(1,n,0))),因为不知道最后关灯人在左边最优还是右边最优。
代码:
#include<bits/stdc++.h>
using namespace std;
const int maxn = 60;
int n, c, ans;
int p[maxn], v[maxn], sum[maxn];
int f[maxn][maxn][3];
int main()
{
scanf("%d%d", &n, &c);
for(int i = 1; i <= n; i++)
scanf("%d%d", &p[i], &v[i]);
for(int i = 1; i <= n; i++)
sum[i] = sum[i-1] + v[i];
memset(f, 0x3f, sizeof f);
f[c][c][0] = f[c][c][1] = 0;
for(int len = 2; len <= n; len++)
for(int l = 1; l <= n - len + 1; l++)
{
int r = len + l - 1;
f[l][r][0] = min(f[l+1][r][0]+(p[l+1]-p[l])*(sum[l]+sum[n]-sum[r]), f[l+1][r][1]+(p[r]-p[l])*(sum[l]+sum[n]-sum[r]));
f[l][r][1] = min(f[l][r-1][0]+(p[r]-p[l])*(sum[l-1]+sum[n]-sum[r-1]), f[l][r-1][1]+(p[r]-p[r-1])*(sum[l-1]+sum[n]-sum[r-1]));
} cout << min(f[1][n][0], f[1][n][1]) << endl;
return 0;
}