洛谷1220:关路灯

题意描述:

  • 一条路线上安装了\(n\)盏路灯,每个灯有功率和位置。
  • 关灯人每天早上需要关灯,他起始处于路灯\(c\)的位置处。
  • 问最小消耗功率的关灯的方案的功率。

输入描述:

  • 第一行输入一个\(n\)\(c\)表示路灯数和关灯人起始位置。
  • 接下来输入每盏灯的位置和功率。

数据范围:

  • \(1\leq n\leq 50,1\leq c\leq 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\)表示前缀和数组。
  • 最后\(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;
}
12-23 11:52