POJ1821-Fence

来源:互联网 发布:进出口海关数据 编辑:程序博客网 时间:2024/05/01 18:54

这是一道经典的单调队列优化DP.
首先不难看出要用动态规划求解,dp[i][j]表示第i个工人粉刷第j块木板时的最大收益,由此
dp[i][j] = max(
dp[i-1][j] 第i个工人不刷木板
dp[i-1][k] + (j - k) * p 第i个工人从第k+1块木板开始一直刷到第j块
)
由于每个工人粉刷的木板必须包含s,因此dp[i][…]只跟dp[i-1][…]有关。
另外前两个方程已经是O(1)时间的,而第三个方程可以变为
max(dp[i-1][k] - k * p) + j * p,因此我们将括号内的数值用一个单调队列维护,然后枚举i,j,k即可。
维护单调队列时我们保证val(dp[i-1][k] - k * p)从大到下,pos从小到大,从队尾不断删除比要插入的小的值,查询时从队首不断抛出,第一个满足位置区间的即是最大的满足条件的val。
注意k是已经粉刷完毕的最后一块木板,k+1是该工人需要粉刷的第一块木板,而j是最后一块需要粉刷的木板:
s - l + 1 <= k + 1 <= s
s - l <= k < s
s <= j <= j + l - 1
搞定需要枚举的区间后剩下的就很简单了。

#include <cstdio>#include <deque>#include <algorithm>using namespace std;const int maxn = 160000 + 10;const int maxm = 100 + 10;struct worker {    int l, p, s;    bool operator < (const worker& w) const {        return s < w.s;    }};worker a[maxm];int dp[maxm][maxn];// dp[i][j] = dp[i-1][k] + (j-k) * pint main(int argc, char const *argv[]) {    int n, m;    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);    for (int i = 1; i <= m; i++) {        for (int j = 1; j <= n; j++) {            dp[i][j] = dp[i-1][j];        }        //first == val, second == pos        deque<pair<int, int> > deq;        for (int k = max(a[i].s - a[i].l, 0); k < a[i].s; k++) {            int val = dp[i-1][k] - k * a[i].p;            while (!deq.empty() && deq.back().first <= val) {                deq.pop_back();            }            deq.push_back(make_pair(val, k));        }        for (int j = a[i].s; j < a[i].s + a[i].l; j++) {            while (!deq.empty() && deq.front().second < j - a[i].l) {                deq.pop_front();            }            dp[i][j] = max(dp[i][j],deq.front().first + j * a[i].p);        }    }    printf("%d\n", *max_element(dp[m] + 1, dp[m] + n + 1));    return 0;}
0 0