决策单调性Ⅰ:四边形不等式:
http://blog.csdn.net/jaihk662/article/details/78174717
决策单调性:
也就是说每个决策点能决策的区间一定是连续的一段,并且随着决策点的右移,这个区间也在不断右移,这样就可以用栈或者二分来O(nlogn)解决原本O(n²)的DP,而斜率优化可以处理一些特殊的单调性DP,并且能做到O(n)的复杂度
还是这个例子:1010: [HNOI2008]玩具装箱toy
这题的dp方程为:dp[i] = min(dp[j]+(sum[i]-sum[k]+i-j-1-L)²)(其中sum[]是前缀和,单调递增)
设A[i] = sum[i]+i-1-L;B[i] = sum[i]+i
有dp[i] = min(dp[j]+(A[i]-B[j])²) = A[i]²+min(-2A[i]B[j]+B[j]²+dp[j])
再设a[i] = -2A[i],x(j) = B[j],y(j) = B[j]²+dp[j]
那么方程就变成了经典形式:f(i) = min(a[i]*x(j)+y(j)),也就是G = ax+y(y = -ax+G)
而我们的目的就是找到一组(x, y)使得G最小
这就相当于空间中有若干个点(x', y'),并且有一条斜率已经确定的直线,让这条直接经过某个点,使得直线与y轴的截距最小,又因为A[i]单调递增的缘故,a = -2A[i],斜率2A[i]一定是单调递增的,同理x和y也单调递增
假设n=6,建模如下图:
将图中的点从左到右依次编号1,2,3,4,5,6,对应着6个决策点(别忘了还有0号点就是坐标原点)
而图中的两条绿线分别是斜率为2a[1]和2a[3]的第1,3条直线,其它直线省略未画出
DP过程如下:
先假设大于,也就是当前还是0号点还是最优决策,情况如下(下图就是大于的情况)
这样搞一趟下来,由于每个点只会进栈出栈1次,所以成功在O(n)时间内求出了所有的最优决策!
下面就是道模板题
Time Limit: 10 Sec Memory Limit: 162 MB
Submit: 5286 Solved: 1961
[Submit][Status][Discuss]
农夫John准备扩大他的农场,他正在考虑N (1 <= N <= 50,000) 块长方形的土地. 每块土地的长宽满足(1 <= 宽 <= 1,000,000; 1 <= 长 <= 1,000,000). 每块土地的价格是它的面积,但FJ可以同时购买多快土地. 这些土地的价格是它们最大的长乘以它们最大的宽, 但是土地的长宽不能交换. 如果FJ买一块3x5的地和一块5x3的地,则他需要付5x5=25. FJ希望买下所有的土地,但是他发现分组来买这些土地可以节省经费. 他需要你帮助他找到最小的经费.
* 第1行: 一个数: N
* 第2..N+1行: 第i+1行包含两个数,分别为第i块土地的长和宽
* 第一行: 最小的可行费用.
4
100 1
15 15
20 5
1 100
500
首先一个显而易见的贪心,所有的土地按x[]从小到大排序,x[]相同就按y[]从小到大排序
这样下来每一组的所有土地一定是连续的
有dp方程:dp[now] = min(dp[now], dp[j]+x[now]*max(y[i], i∈(j+1, now)) )
理论上可以斜率优化时线段树维护最大值,但这样挺麻烦的
考虑再贪心:如果排序后某一块土地的x[]值小于它后面一块(其实这个是肯定的都排了序)并且y[]值也小于它后面一块,那么这一块就可以无视,因为这样的话你只用多选它后面那一块其实就ok了,这块就一定没有贡献,有贡献就一定不是最优解
这样处理完毕之后整个序列就一定满足x递增的同时y递减,dp方程变为
dp[now] = min(dp[now], dp[j]+x[now]*y[j+1])
将其转化成标准形式:y = -ax+G有a = x[now];x = y[j+1];y = dp[j]
因为斜率k和x单调递减,y单调递增,所以要维护上凸包,有方程
-x[now]<(dp[j]-dp[k])/(y[j+1]-y[k+1]),就说明k点比j点更优
#include
#include
using namespace std;
#define LL long long
typedef struct Res
{
LL x, y;
bool operator < (const Res &b) const
{
if(x=1 && s[i].y>=s[cnt].y)
cnt--;
s[++cnt] = s[i];
}
n = cnt;
L = R = 1;
for(i=1;i<=n;i++)
{
while(R>L && Calc(st[L], st[L+1])>-s[i].x)
L++;
dp[i] = dp[st[L]]+s[i].x*s[st[L]+1].y;
while(R>L && Calc(st[R], i)>Calc(st[R-1], st[R]))
R--;
st[++R] = i;
}
printf("%lld\n", dp[n]);
return 0;
}