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\n", x)
#define Prl(x) printf("%lld\n",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\n", x)
#define Prl(x) printf("%lld\n",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表快一些

原文地址:https://www.cnblogs.com/Hugh-Locke/p/9643832.html

时间: 2024-11-08 21:51:16

POJ1821 单调队列//ST表 优化dp的相关文章

HDU 4123 Bob&#39;s Race:单调队列 + st表

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4123 题意: 给你一棵树,n个节点,每条边有长度. 然后有m个询问,每个询问给定一个q值. 设dis[i]为:从节点i出发,不重复经过节点,所能够走的最远距离. 每次询问问你:区间[l,r]最长能有多长,同时保证 max{dis[i]} - min{dis[i]} <= q (i∈[l,r]) 题解: 首先有一个结论: 从树上的任意一个节点出发,尽可能往远走,最终一定会到达树的直径的两个端点之一.

APIO2010特别行动队(单调队列、斜率优化)

其实这题一看知道应该是DP,再一看数据范围肯定就是单调队列了. 不过我还不太懂神马单调队列.斜率优化-- 附上天牛的题解:http://www.cnblogs.com/neverforget/archive/2012/04/19/2456483.html 1 var f,g:array[0..1000050] of int64; 2 s,q:array[0..1000050] of longint; 3 a,b,c,n,i,h,t,x:longint; 4 bestk:double; 5 pro

斜率优化DP总结

前言: 也是好久没有写题解了,最近主要学习了单调栈单调队列以及斜率优化DP这几个知识点,对于较难的斜率优化DP,做个小小的总结吧. 正(che)文(dan): T1 hdu 3507 在一个风和日丽的早上,你打开了网页,点进了hdu,偶然间看到了这道题,不屑的以为这仅仅是一个很水的DP,2分钟给出DP方程式,很快的写完后发现n的范围居然是500000,这让已经推出来的 O(n2)复杂度的递推式情何以堪,所以就产生了一种高逼格的优化方式:斜率优化. 这道题的方程式是什么呢? dp[i]=min(d

【单调队列优化DP】BZOJ1855-[Scoi2010]股票交易

[题目大意] 已知第i天的股票买入价为每股APi,第i天的股票卖出价为每股BPi(数据保证对于每个i,都有APi>=BPi),第i天的一次买入至多只能购买ASi股,一次卖出至多只能卖出BSi股. 股票交易所规定在两次交易(某一天的买入或者卖出均算是一次交易)之间,至少要间隔W天,也就是说如果在第i天发生了交易,那么从第i+1天到第i+W天,均不能发生交易.同时,在任何时间,一个人的手里的股票数不能超过MaxP. 在第1天之前,有一大笔钱(可以认为钱的数目无限),没有任何股票,求T天之后最多赚到多

烽火传递【单调队列优化dp】

题目大意: 1.给出长度为n的数组,要求每m个连续的元素之间必须选一个值作为代价,求该数组的最小代价. 题解思路: 1.显然是线性dp,dp[i]表示选择第 i 个元素时的最小总代价.很明显状态转移方程为 dp[i] = min(dp[j]) + a[i].(i - m <= j <= i - 1).但是在求min(dp[j])的时候,我们需要遍历一遍长度为m大小的区间,m极限与n同大.时间负责度0(N^2).显然会超时. 2.我们需要用单调队列来维护长度为m大小区间内的最小值,队头即为最小值

poj3017 dp+单调队列

http://poj.org/problem?id=3017 Description Given an integer sequence { an } of length N, you are to cut the sequence into several parts every one of which is a consecutive subsequence of the original sequence. Every part must satisfy that the sum of

POJ1821 Fence 单调队列优化DP

http://poj.org/problem?id=1821 题意:给长度为n的木板,k个工人,每个工人要么不粉刷,或者选择一个包含木板si,长度不超过li的连续的一段木板粉刷,每粉刷一块得到pi的报酬,问如何安排工人使得总报酬最大? 思路:可以按si给工人排序,这样我们就可以按照顺序依次安排工人.设f[i][j]表示到第i个工人,刷到前j块木板的最大报酬, 三种情况 工人不刷:f[i][j]=f[i-1][j] 木板空着:f[i][j]=f[i][j-1] 第i个工人刷k+1到j的木板,其中k

$Poj3017\ Cut\ The\ Sequence$ 单调队列优化$DP$

Poj   AcWing Description 给定一个长度为N的序列 A,要求把该序列分成若干段,在满足“每段中所有数的和”不超过M的前提下,让“每段中所有数的最大值”之和最小. N<=105,M<=1011,0<Ai<=106 Sol 一篇比较清楚的题解 $OvO$ $F[i]$表示把前$i$个数分成若干段,满足每段中所有数之和不超过$M$的前提下,各段的最大值之和的最小值 不难推出转移方程: 但是直接枚举$j$的做法是$O(N^{2})$的,显然过不去,还要优化. DP转移

HDU 4122 Alice&#39;s mooncake shop 单调队列优化dp

Alice's mooncake shop Time Limit: 20 Sec Memory Limit: 256 MB 题目连接 http://acm.hdu.edu.cn/showproblem.php?pid=4122 Description The Mid-Autumn Festival, also known as the Moon Festival or Zhongqiu Festival is a popular harvest festival celebrated by Ch