洛谷 P1220 关路灯 区间dp

题面

https://www.luogu.com.cn/problem/P1220

分析

考虑区间\(dp\)

\(dp[l][r][0]\)为已经关了\([l,r]\)的灯,当前位置在\(l\)的最少功耗;

\(dp[l][r][1]\)为已经关了\([l,r]\)的灯,当前位置在\(r\)的最少功耗。

可以发现,因为状态是区间的扩大,故只有如下情况:

若当前为\(dp[l][r][0]\),上一个状态要么是从\(l+1\)点向左走了\(1\)格,即\(dp[l+1][r][0]+cost(l,l+1)\);或者是从\(r\)点向左走了\(r-l+1\)格,即\(dp[l+1][r][1]+cost(l,r)\)

若当前为\(dp[l][r][1]\),上一个状态要么是从\(r-1\)点向右走了\(1\)格,即\(dp[l][r-1][1]+cost(r-1,r)\);或者是从\(l\)点向右走了\(r-l+1\)格,即\(dp[l][r-1][0]+cost(l,r)\)

考虑\(cost\)的计算:

可以发现,cost的计算与跑动的时间、目前仍在运行的灯数均有关。当从\(x\)跑到\(y\)时,且当前\([l,r]\)的灯已被关闭时:

目前仍在运行的灯数(功率)为\(sum[l-1]+(sum[n]-sum[r])\),跑动时间为\((y-x)\)

故cost函数可以写为:

int count(int x, int y, int l, int r) {
    return (pos[y] - pos[x]) * (sum[l - 1] + (sum[n] - sum[r]));
}

Code

#include<iostream>
#include<cstdio>
#include<cstring>

const int maxn = 50 + 10;
const int inf = 0x3f3f3f3f;
int n, c;
int pos[maxn], p[maxn], sum[maxn];
int dp[maxn][maxn][2];

int count(int x, int y, int l, int r) {
    return (pos[y] - pos[x]) * (sum[l - 1] + (sum[n] - sum[r]));
}

int main() {
    memset(dp, inf, sizeof(dp));
    scanf("%d%d", &n, &c);
    for (int i = 1; i <= n; i++) {
        scanf("%d%d", &pos[i], &p[i]);
        sum[i] = sum[i - 1] + p[i];
        //dp[i][i][0]=dp[i][i][1]=0;
    }
    dp[c][c][0] = dp[c][c][1] = 0;
    for (int len = 2; len <= n; len++)
        for (int i = 1; i + len - 1 <= n; i++) {
            int j = i + len - 1;
            dp[i][j][0] = std::min(dp[i + 1][j][0] + count(i, i + 1, i + 1, j),
                                   dp[i + 1][j][1] + count(i, j, i + 1, j));
            dp[i][j][1] = std::min(dp[i][j - 1][1] + count(j - 1, j, i, j - 1),
                                   dp[i][j - 1][0] + count(i, j, i, j - 1));
        }
    int ans = std::min(dp[1][n][0], dp[1][n][1]);
    printf("%d", ans);
    return 0;
}

posted @ 2022-11-08 13:41  SxtoxA  阅读(23)  评论(0)    收藏  举报
12 13