POJ1821 单调队列//ST表 优化dp

Posted hugh-locke

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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表快一些

以上是关于POJ1821 单调队列//ST表 优化dp的主要内容,如果未能解决你的问题,请参考以下文章

POJ1821 Fence 单调队列优化DP

ybtoj 单调队列课堂过关 例题2POJ 1821DP粉刷木板 & Fence

0x59 单调队列优化DP

POJ2373 Dividing the Path(单调队列优化dp)

POJ 1180 斜率优化DP(单调队列)

POJ1821Fence