P1220 关路灯(简单dp 优先队列)

原题: https://www.luogu.org/problemnew/show/P1220

题意:

n盏路灯,每盏灯的功率w,位置pos。老张一盏一盏关掉这些路灯。
首先关掉自己所处位置的路灯,然后可以向左也可以向右去关灯。
已知老张走的速度为1m/s,每个路灯的位置、功率。求关闭所有灯的最小花费。

解析:

因为没关的灯肯定集中在两边,所以可以用 d p [ x ] [ y ] [ 0 ∣ 1 ] dp[x][y][0|1] dp[x][y][01]代表当前没关的灯为1~x,y~n,为0表示刚关x,1刚关y。因为用for循环不好写,所以借助优先队列,以 a b s ( x − y ) abs(x-y) abs(xy)从小到大排序。

这样花费也很好维护,哪些灯没关可以直接得出。

dp初值设为inf。

#include
using namespace std;

const int N = 55;
int pos[N], w[N];
int L[N], R[N];
int dp[N][N][2];
struct node {
    int x, y, f;
    node() {}
    node(int x, int y, int f): x(x), y(y), f(f) {}
    bool operator <(const node r)const {
        return abs(x - y) > abs(r.x - r.y);
    }
};

void UP(priority_queue<node>&Q, int val, int x, int y, int f) {
    if(dp[x][y][f] == 1e9) {
        dp[x][y][f] = val;
        Q.push(node(x, y, f));
    } else if(dp[x][y][f] > val) {
        dp[x][y][f] = val;
    }
}

int main() {
    int n, p;
    scanf("%d%d", &n, &p);
    for(int i = 1; i <= n; i++) {
        scanf("%d%d", pos + i, w + i);
    }
    
    L[1] = w[1];
    for(int i = 2; i <= n; i++)
        L[i] = L[i - 1] + w[i];
    R[n] = w[n];
    for(int i = n - 1; i >= 1; i--)
        R[i] = R[i + 1] + w[i];
        
    for(int i = 0; i < N; i++)
        for(int j = 0; j < N; j++)
            dp[i][j][0] = dp[i][j][1] = 1e9;

    dp[p - 1][p][1] = dp[p][p + 1][0] = 0;
    priority_queue<node>Q;
    Q.push(node(p, p + 1, 0));
    
    while(!Q.empty()) {
        int x = Q.top().x, y = Q.top().y, f = Q.top().f, val;
        Q.pop();
        if(f == 0) {
            if(x > 1) { // 往左
                val = dp[x][y][f] + (pos[x] - pos[x - 1]) * (L[x - 1] + R[y]);
                UP(Q, val, x - 1, y, 0);
            }
            if(y <= n) { // 往右
                val = dp[x][y][f] + (pos[y] - pos[x]) * (L[x - 1] + R[y]);
                UP(Q, val, x - 1, y, 1);
            }
        } else {
            if(x >= 1) { // 往左
                val = dp[x][y][f] + (pos[y] - pos[x]) * (L[x] + R[y + 1]);
                UP(Q, val, x, y + 1, 0);
            }
            if(y < n) { // 往右
                val = dp[x][y][f] + (pos[y + 1] - pos[y]) * (L[x] + R[y + 1]);
                UP(Q, val, x, y + 1, 1);
            }
        }
    }
    printf("%d\n", min(dp[1][n + 1][0], dp[0][n][1]));
}

你可能感兴趣的:(DP动态规划)