Loading

POJ 1821 Fence(单调队列优化DP)

大意:
有 N 块连续的木板,并有 K 个工人来粉刷,但不要求全部粉刷。每个工人有三个参数:L,P,S,表示其最多粉刷连续的 L 块木板,并且每粉刷一块木板可获得 P 元,但所粉刷的木板必须包括第 S 块。输出所能获得最大价值。
思路:
dp[i][j] 表示第 i 个人粉刷到第 j 块木板,所获得最大价值。
dp[i][j] = max{ dp[i][j-1] , dp[i-1][j] , dp[i-1][k] + (j-k)·p[i] }
分别表示:第 i 个人不刷、第 j 面墙不刷、第 i 个人刷 k+1 到 j 块木板。且对于第三种,需满足 j-l[i]<=k<=s[i]-1 和 s[i]<=j<=s[i]+l[i]-1。
对于第三项来说,需要进行优化,可以发现转化为 dp[i-1][k]+k×p[i] 和 j×P[i] 这两项,后一项是常数,那么只需要单调队列求出前面的最大值即可

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;

const int MAXM = 200;
const int MAXN = 2e5 + 5;
typedef long long LL;

struct node {
    int l, p, s;
} a[MAXM];
int n, m, le[MAXM], re[MAXM], q[MAXM], dp[MAXM][MAXN];
bool cmp(const node &a, const node &b) { return a.s < b.s; }
int main() {
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= m; i++) scanf("%d%d%d", &a[i].l, &a[i].p, &a[i].s);
    sort(a + 1, a + m + 1, cmp);
    for (int i = 1; i <= m; i++) {
        le[i] = max(0, a[i].s - a[i].l);      
        re[i] = min(n, a[i].s + a[i].l - 1);  
    }
    for (int i = 1; i <= m; i++) {
        int l = a[i].l;
        int p = a[i].p;
        int s = a[i].s;
        for (int j = 0; j <= re[i]; j++)
            dp[i][j] = dp[i - 1][j]; 
        int h = 1;
        int t = 0;
        for (int j = le[i]; j < a[i].s; j++) {  //维护单调队列
            while (h <= t && dp[i - 1][j] - j * p >= dp[i - 1][q[t]] - q[t] * p)
                t--;
            q[++t] = j;
        }
        for (int j = s; j <= re[i]; j++) {
            while (h < t && j - q[h] > l) h++;  
            dp[i][j] = max(dp[i][j - 1], dp[i - 1][j]);
            dp[i][j] = max(dp[i][j], dp[i - 1][q[h]] + (j - q[h]) * p);
        }
        for (int j = re[i] + 1; j <= n; j++)
            dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
    }
    int ans = 0;
    for (int i = 1; i <= n; i++) ans = max(ans, dp[m][i]);
    printf("%d\n", ans);
    return 0;
}
posted @ 2021-03-15 00:20  WinterFa1L  阅读(64)  评论(0编辑  收藏  举报