zoukankan      html  css  js  c++  java
  • POJ1821 单调队列//ST表 优化dp

    http://poj.org/problem?id=1821

    当我们在考虑内层循环j以及决策k的时候,我们可以把外层变量i看作定值,以此来优化dp状态转移方程。

    题意 有n个工人准备铺m个连续的墙,每个工人有他必须图的一面墙壁Si,最多连续铺Li,每铺一个就花费Ci的钱,问最多要多少钱;

    朴素算法很好想,就dp[i][j]维护i工人到这j层墙壁的最大值,对于每个工人去枚举他涂墙壁的开头和结尾然后更新即可。

    时间复杂度O(NMM) M的范围是16000,很显然会T,我们考虑状态转移方程。

    对于每个工人,dp[i][j]的更新是寻找一个k使得dp[i - 1][k - 1] + (j - k + 1 ) * P 最大;

    在这个转移方程里,我们将i看作定值,除了状态变量j之外还有一个决策j,看似很难处理,我们将方程变形.

    dp[i][j]的更新变为 max(dp[i - 1][k - 1] - (k - 1) * P) + j * P;

    在这一层中,最大值的寻找仅和k有关,而k事实上对每一个i都是可以预处理出来的,在j查询的时候只有范围变动,问题就变成了常规的优化区间最大值的问题。

    这里附上用ST表优化和单调队列优化的两种方法。

    #include <map>
    #include <set>
    #include <ctime>
    #include <cmath>
    #include <queue>
    #include <stack>
    #include <vector>
    #include <string>
    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #include <sstream>
    #include <iostream>
    #include <algorithm>
    #include <functional>
    using namespace std;
    #define For(i, x, y) for(int i=x;i<=y;i++)  
    #define _For(i, x, y) for(int i=x;i>=y;i--)
    #define Mem(f, x) memset(f,x,sizeof(f))  
    #define Sca(x) scanf("%d", &x)
    #define Sca2(x,y) scanf("%d%d",&x,&y)
    #define Scl(x) scanf("%lld",&x);  
    #define Pri(x) printf("%d
    ", x)
    #define Prl(x) printf("%lld
    ",x);  
    #define CLR(u) for(int i=0;i<=N;i++)u[i].clear();
    #define LL long long
    #define ULL unsigned long long  
    #define mp make_pair
    #define PII pair<int,int>
    #define PIL pair<int,long long>
    #define PLL pair<long long,long long>
    #define pb push_back
    #define fi first
    #define se second 
    typedef vector<int> VI;
    const double eps = 1e-9;
    const int maxn = 110;
    const int maxm = 20010;
    const int INF = 0x3f3f3f3f;
    const int mod = 1e9 + 7; 
    int N,M,tmp,K; 
    inline int read()
    {
       int now=0;register char c=getchar();
       for(;!isdigit(c);c=getchar());
       for(;isdigit(c);now=now*10+c-'0',c=getchar());
       return now;
    }
    struct Node{
        int L,P,S;
    }node[maxn];
    int dp[2][maxm];
    bool cmp(Node a,Node b){
        return a.S < b.S;
    }
    int DP[maxm][20];
    int mm[maxm];
    int num[maxm];
    void initRMQ(int n,int b[]){
        mm[0] = -1;
        for(int i = 1; i <= n ; i ++){
            mm[i] = ((i & (i - 1)) == 0) ? mm[i - 1] + 1:mm[i - 1];
            DP[i][0] = b[i];
        }
        for(int j = 1; j <= mm[n]; j ++){
            for(int i = 1; i + (1 << j) - 1 <= n ; i++){
                DP[i][j] = max(DP[i][j - 1],DP[i + (1 << (j - 1))][j - 1]);
            }
        }
    }
    int rmq(int x,int y){
        int k = mm[y - x + 1];
        return max(DP[x][k],DP[y - (1 << k) + 1][k]);
    }
    int main()
    {
        while(~Sca2(N,K)){
            For(i,1,K){
                scanf("%d%d%d",&node[i].L,&node[i].P,&node[i].S);
            }
            sort(node + 1,node + 1 + K,cmp);
            Mem(dp,0);
            For(i,1,K){
                Mem(num,0);
                Mem(dp[i & 1],0);
                for(int k = max(node[i].S - node[i].L + 1,1); k <= node[i].S; k ++){
                    num[k] = dp[i - 1 & 1][k - 1] - node[i].P * (k - 1);
                }
                initRMQ(node[i].S,num);
                For(j,1,N){
                    dp[i & 1][j] = max(dp[i - 1 & 1][j],dp[i & 1][j - 1]);
                    if(j >= node[i].S && j <= node[i].S + node[i].L - 1){
                        dp[i & 1][j] = max(dp[i & 1][j],rmq(max(j - node[i].L + 1,1),node[i].S) + node[i].P * j);
                    }
                } 
            }
            Pri(dp[K & 1][N]);
        }
        #ifdef VSCode
        system("pause");
        #endif
        return 0;
    }
    ST表优化
    #include <map>
    #include <set>
    #include <ctime>
    #include <cmath>
    #include <queue>
    #include <stack>
    #include <vector>
    #include <string>
    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #include <sstream>
    #include <iostream>
    #include <algorithm>
    #include <functional>
    using namespace std;
    #define For(i, x, y) for(int i=x;i<=y;i++)  
    #define _For(i, x, y) for(int i=x;i>=y;i--)
    #define Mem(f, x) memset(f,x,sizeof(f))  
    #define Sca(x) scanf("%d", &x)
    #define Sca2(x,y) scanf("%d%d",&x,&y)
    #define Scl(x) scanf("%lld",&x);  
    #define Pri(x) printf("%d
    ", x)
    #define Prl(x) printf("%lld
    ",x);  
    #define CLR(u) for(int i=0;i<=N;i++)u[i].clear();
    #define LL long long
    #define ULL unsigned long long  
    #define mp make_pair
    #define PII pair<int,int>
    #define PIL pair<int,long long>
    #define PLL pair<long long,long long>
    #define pb push_back
    #define fi first
    #define se second 
    typedef vector<int> VI;
    const double eps = 1e-9;
    const int maxn = 110;
    const int maxm = 20010;
    const int INF = 0x3f3f3f3f;
    const int mod = 1e9 + 7; 
    int N,M,tmp,K; 
    inline int read()
    {
       int now=0;register char c=getchar();
       for(;!isdigit(c);c=getchar());
       for(;isdigit(c);now=now*10+c-'0',c=getchar());
       return now;
    }
    struct Node{
        int L,P,S;
    }node[maxn];
    int dp[2][maxm];
    bool cmp(Node a,Node b){
        return a.S < b.S;
    }
    int Queue[maxm];
    int head,tail;
    int main()
    {
        while(~Sca2(N,K)){
            For(i,1,K){
                scanf("%d%d%d",&node[i].L,&node[i].P,&node[i].S);
            }
            sort(node + 1,node + 1 + K,cmp);
            Mem(dp,0);
            For(i,1,K){
                head = 1; tail = 0;
                Mem(dp[i & 1],0);
                for(int k = max(node[i].S - node[i].L + 1,1); k <= node[i].S; k ++){
                    int ans = dp[i - 1 & 1][k - 1] - node[i].P * (k - 1);
                    while(head <= tail && dp[i - 1 & 1][Queue[tail] - 1] - node[i].P * (Queue[tail] - 1)<= ans) tail--;
                    Queue[++tail] = k;
                }
                For(j,1,N){
                    dp[i & 1][j] = max(dp[i - 1 & 1][j],dp[i & 1][j - 1]);
                    if(j >= node[i].S){
                        while(head <= tail && Queue[head] < j - node[i].L + 1) head++;
                        if(head <= tail) dp[i & 1][j] = max(dp[i & 1][j],dp[i - 1 & 1][Queue[head] - 1] + (j - Queue[head] + 1) * node[i].P);
                    }
                } 
            }
            Pri(dp[K & 1][N]);
        }
        #ifdef VSCode
        system("pause");
        #endif
        return 0;
    }
    单调队列优化

    值得一提的是单调队列的查询和处理的时间都是线性的,总时间复杂度为O(NM),而ST表的预处理要用到nlnn,所以用时会比ST表快一些

  • 相关阅读:
    zabbix源码安装
    利用Linux系统生成随机密码的8种方法
    Java 内存溢出(java.lang.OutOfMemoryError)的常见情况和处理方式总结
    Jenkins的参数化构建
    Jenkins中maven的作用--构建项目(三)
    Beans(dp,两次dp)
    Piggy-Bank(完全背包)
    Super Jumping! Jumping! Jumping!(dp)
    01串(dp)
    钱币兑换问题(完全背包)
  • 原文地址:https://www.cnblogs.com/Hugh-Locke/p/9643832.html
Copyright © 2011-2022 走看看