最近做了一些单调队列优化DP的题目,现在总结一下。
遇到这类题目首先要能够想出朴素的DP状态转移方程,然后对其进行相应的转化,对于已知的部分用单调队列来维护。以下附几道题目,部分题解摘自别人的解题报告。(自己懒的写啦。其实是我觉得别人写的比我好T_T)
HDU 3401 Trade http://acm.hdu.edu.cn/showproblem.php?pid=3401
题意:给你T天股票的信息,每天的信息分别可以表示为:APi , BPi , ASi , BSi,分别表示第i天时股票的买入单价、卖出单价、第i天最多可以买入的股票数、最多可以卖出的股票数,并且还有一个约束条件,那就是两个交易日相距的天数要大于W天,问最终最多能
获利多少。
分析:
朴素的状态转移方程:
购买 :dp[i][j] = max {dp[pre][k] - (j - k) * b[i] }; 其中 0 < j - k < bnum[i] (pre代表j之前某天)
卖出 :dp[i][j] = max {dp[pre][k] + (k - j) * s[i] }; 其中 0 < k - j < snum[i]
不买也不卖:dp[i][j] = max {dp[i][j], dp[i - 1][j] };
时间复杂度为:O( maxP ^ 2 * T ^ 2)显然要超时。
对于购买状态转移方程可以转化为:dp[i][j]=max(dp[pre][k]+k*b[i]-j*b[i]),对于dp[pre][k]+k*b[i]已确定的情况可以用单调对列来处理。对于卖出的情况同理。
Source Code:
#include
#include
using namespace std;
#define min(a,b) ((a)<(b)?(a):(b))
#define max(a,b) ((a)>(b)?(a):(b))
const int inf=0x3f3f3f3f;
const int maxn=2010;
int dp[maxn][maxn],bp[maxn],sp[maxn],bnum[maxn],snum[maxn];
struct node{
int mon,num;
}q[maxn];
int main()
{
int t,n,m,w,k,i,j;
scanf("%d",&t);
while(t--){
scanf("%d %d %d",&n,&m,&w);
for(i=1;i<=n;i++)
scanf("%d %d %d %d",&bp[i],&sp[i],&bnum[i],&snum[i]);
for(i=0;i<=n;i++){//初始化
for(j=0;j<=m;j++)
dp[i][j]=-inf;
}
for(i=1;i<=m;i++){//预处理
dp[i][0]=0;
for(j=1;j<=bnum[i];j++)
dp[i][j]=-j*bp[i];
}
for(i=2;i<=n;i++){//DP
for(j=0;j<=m;j++)//不买也不卖
dp[i][j]=max(dp[i][j],dp[i-1][j]);
if(i=0;j--){
while(lj) l++;
//若卖掉第i天最多能卖的股票还是达不到j个,则剔除对首
if(l
UESTC 1685 我要长高 http://www.acm.uestc.edu.cn/problem.php?pid=1685
(此题转自:http://www.cnblogs.com/ka200812/archive/2012/07/11/2585950.html)
韩父有N个儿子,分别是韩一,韩二…韩N。由于韩家演技功底深厚,加上他们间的密切配合,演出获得了巨大成功,票房甚至高达2000万。舟子是名很有威望的公知,可是他表面上两袖清风实则内心阴暗,看到韩家红红火火,嫉妒心遂起,便发微薄调侃韩二们站成一列时身高参差不齐。由于舟子的影响力,随口一句便会造成韩家的巨大损失,具体亏损是这样计算的,韩一,韩二…韩N站成一排,损失即为C*(韩i与韩i+1的高度差(1<=i 有若干组数据,一直处理到文件结束。 每组数据第一行为两个整数:韩子数量N(1<=N<=50000)和舟子系数C(1<=C<=100) 接下来N行分别是韩i的高度(1<=hi<=100)。 首先建立方程,很容易想到的是,dp[i][j]表示第 i 个儿子身高为 j 的最低花费。分析题目很容易知道,当前儿子的身高花费只由前一个儿子影响。因此, dp[i][j]=min(dp[i-1][k] + abs(j-k)*C + (x[i]-j)*(x[i]-j));其中x[i]是第i个儿子原本的身高 我们分析一下复杂度。 首先有N个儿子,这需要一个循环。再者,每个儿子有0到100的身高,这也需要一维。再再者,0到100的每一个身高都可以有前一位儿子的身高0到100递推而来。 所以朴素算法的时间复杂度是O(n^3)。题目只给两秒,难以接受! 分析方程: 当第 i 个儿子的身高比第 i-1 个儿子的身高要高时, dp[i][j]=min(dp[i-1][k] + j*C-k*C + X); ( k<=j ) 其中 X=(x[i]-j)*(x[i]-j)。 当第 i 个儿子的身高比第 i-1 个儿子的身高要矮时, dp[i][j]=min(dp[i-1][k] - j*C+k*C + X); ( k>=j ) 对第一个个方程,我们令 f[i-1][k]=dp[i-1][k]-k*C, g[i][j]=j*C+X; 于是 dp[i][j] = min (f[i-1][k])+ g[i][j]。转化成这样的形式,我们就可以用单调队列进行优化了。 第二个方程同理。 Source Code: 分析:对于 左边我们可以得到 dp[i][j] = max( dp[i - 1][k] + sum(k, j)) j - t <=k <= j; 我们的队列 要维护的 就是 dp[i - 1][k] + sum(k, j) 由于 sum (k ,j) 单调队列要维护的状态必须是和 现在的状态 无关(或着说是 以知的),但 sum(k,j) 用到了现在的状态; 所以 我们要进行转换一下, sum(k,j) = sum (1,j) - sum (1,k - 1); 所以 原式变为 右边同理 可得 Source Cede: POJ 1821 Fence http://poj.org/problem?id=1821 题意:N面墙,K个人来粉刷,每个人有一个粉刷数量的限制,且各个人粉刷一面墙的价格不同,每个人的初始位置在某面墙前,且该面墙或要由他来刷,或由其他的人刷,或不刷,问粉刷这N面墙可以得到的最大的价值。 分析:很容易可以想到该题的状态转移方程: dp[i][j]= max { dp[i-1][j](不用该工人), dp[i][j-1] (不刷这面墙), dp[i - 1][k] + men[i].price*(j - k) 从第k+1面墙刷到第j面 且k >= j - l; }(dp[i][j]表示有前i个人刷到第j面墙可以得到的最大价值)。 对于dp[i-1][k]+men[i].price*(j-k),转化成dp[i-1][k]-k*men[i].price+j*men[i].price,对前i-1个人的最大价值进行单调队列优化。 (这题虽然我很快的想出了状态转移方程,但对于细节的处理还是很搓,一直不对,所以这里的代码参考了:http://hi.baidu.com/yimaolionel/item/4b1155f0b2f23b1fd7ff8c36) Source Code: Input
#include
HDU 4374 One hundred layer http://acm.hdu.edu.cn/showproblem.php?pid=4374
而 dp[i -1][k] - sum(1,k - 1) 相对于 dp[i][j] 来说 是 以知的 所以
#include
#include