zoukankan      html  css  js  c++  java
  • POJ 1821 Fence ★(单调队列优化DP)

    题目大意:有一道线性篱笆由N个连续的木板组成。有K个工人,你要叫他们给木板涂色。每个工人有3个参数:L 表示 这个工人可以涂的最大木板数目,S表示这个工人站在哪一块木板,P表示这个工人每涂一个木板可以得到的钱。要注意的是,工人i可以选择不涂任何木板,否则,他的涂色区域必须是连续的一段,并且S[i]必须包含在内。 最后还有,每块木板只能被涂一次。   状态方程比较容易想:dp[i][j]表示第i个人刷的最后一面墙是j时的最大获利,则 dp[i][j]=max(dp[i-1][k]+(j-k)*p[i]) j-l[i]+1<=k+1<=s[i]         (*) dp[i][j]=max(dp[i-1][j],dp[i][j-1],dp[i][j])//第i个人不刷,第j面墙不刷   但是O(k*n^2)的复杂度显然不能接受。而针对(*)这种“特殊”的转移方程,我们可以用单调队列把它优化到O(1): dp[i][j]=max(dp[i-1][k]-k*p[i])+j*p[i] 其中j*p[i]在i,j两重循环中相当于常数,所以,对于状态dp[i][j]只要单调队列维护dp[i-1][k]-k*p[i]的最大值即可   单调队列维护过程(可以回过头看看POJ 2823---单调队列的模型): 单调队列具体的做法是:最外层循环为i,首先把j=1~s[i]-1转移完(因为它不涉及第三个转移),然后把(j-l[i]<=k<=s[i]-1)的决策点的F[i-1,k]-p[i]*k依次入队建立“入队早晚时间递增,F[i-1,k]-p[i]*k的值递减”的单调队列,接下来循环j=s[i]~s[i]+l[i]-1,进行这三个转移(第三个转移只需要用队首元素),其中每次需要把队首超出长度限制的决策点出队;最后把剩下的到n循环完,只需要前两个转移。  
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #define MID(x,y) ((x+y)>>1)
    using namespace std;
    typedef long long LL;
    struct worker{
        int l, p, s;
    }w[105];
    bool cmp(worker a, worker b){
        return a.s < b.s;
    }
    int f[105][16005];
    int n,k;
    void debug(){
        for (int i = 0; i <= k; i ++){
            for (int j = 0; j <= n; j ++)
                printf("%d %d %d\n", i, j, f[i][j]);
        }
    }
    deque  Q;
    void initQ(){
        while(!Q.empty())
            Q.pop_back();
    }
    int main(){
        scanf("%d %d", &n, &k);
        for (int i = 1; i <= k; i ++)
            scanf("%d %d %d", &w[i].l, &w[i].p, &w[i].s);
        sort(w+1, w+k+1, cmp);  //别忘了先按S排序!
        for (int i = 1; i <= k; i ++){
            for (int j = 0; j < w[i].s; j ++)
                f[i][j] = max(f[i-1][j], f[i][j-1]);
            //单调队列
            initQ();
            for (int j = max(0, w[i].s - w[i].l); j < w[i].s; j ++){
                while(!Q.empty() && f[i-1][Q.back()] - Q.back()*w[i].p <= f[i-1][j] - j*w[i].p)
                    Q.pop_back();
                Q.push_back(j);
            }
            for (int j = w[i].s; j < min(w[i].s + w[i].l, n+1); j ++){
                while (!Q.empty() && Q.front() < j - w[i].l)
                    Q.pop_front();
                f[i][j] = max(f[i-1][j], f[i][j-1]);
                f[i][j] = max(f[i][j], f[i-1][Q.front()] - Q.front()*w[i].p + j * w[i].p);
    //            朴素DP:
    //            for (int p = max(0, j - w[i].l); p <= j; p ++)
    //                f[i][j] = max(f[i][j], f[i-1][p] + (j - p)*w[i].p);
            }
            for (int j = w[i].s + w[i].l; j <=n; j ++)
                f[i][j] = max(f[i-1][j], f[i][j-1]);
        }
        debug();
        int res = 0;
        for (int j = 1; j <= n; j ++)
            res = max(res, f[k][j]);
        printf("%d\n", res);
    	return 0;
    }
    
     
    举杯独醉,饮罢飞雪,茫然又一年岁。 ------AbandonZHANG
  • 相关阅读:
    洛咕11月月赛部分题解 By cellur925
    POJ 2411 Mondriaan's Dream 【状压Dp】 By cellur925
    Luogu P1637 三元上升子序列【权值线段树】By cellur925
    Luogu P1438无聊的序列【线段树/差分】By cellur925
    Luogu P1558 色板游戏【线段树/状态压缩】By cellur925
    Luogu P4403 [BJWC2008]秦腾与教学评估【二分答案】By cellur925
    Luogu P3941 入阵曲【前缀和】By cellur925
    查询事件状态,mysql查看事件是否开启,设置启动时自动开启方法
    Logback详细整理,基于springboot的日志配置
    使用release自动打包发布正式版详细教程
  • 原文地址:https://www.cnblogs.com/AbandonZHANG/p/4114006.html
Copyright © 2011-2022 走看看