[关闭]
@Junlier 2018-09-23T09:12:30.000000Z 字数 2177 阅读 1631

土地征用题解(兼斜率优化详解)

动态规划——斜率优化
推广博客:eternal风度的博客
博客食用(阅读体验):eternal风度的土地征用
这不是以题解为最主要目的的博客谢谢
重点是帮助理解斜率优化

链接题目:洛谷土地征用
这里有的各种优化。。。
这里是的dp优化博客

前期处理及一些小性质

我们这里直接把土地理解成一些矩形,显然如果一个大矩形可以把另一个小矩形包含住,那个小矩形肯定会被并购掉,可以直接不考虑,就要去掉
那么可以在的复杂度内解决掉:

按高度从大到小排序,如果当前的宽度比之前最宽的宽度要小,显然不要;如果要大,就加进需要考虑的矩形数组里,顺便更新宽度最大值

  1. int L=1;
  2. for(int i=1;i<=n;++i)
  3. if(ljl[L].w<ljl[i].w)ljl[++L]=ljl[i];//ljl是定义的矩形的结构体。。。
  4. n=L;

现在我们得到了一个高度单调递减,宽度单调递增的矩形序列对吧
那么是不是很容易发现一定是把一段下标连续的矩形并购更优
简单证明一下:如果并购不连续,那么中间那个断开的高度一定小于前一段,宽度一定小于后一段,把它并购到这一段不连续的里面不会产生费用,所以这种决策不会更差,只会更优。。。

部分

有前面的那个简单性质,可以考虑
表示买完前i块矩形的最小费用
显然有方程:{}
表示我们把区间上的矩形并购了

斜率优化

由来

直接做肯定是的复杂度
考虑优化
那么假设我们有两个可能的转移状态,不妨设
那么假设决策比决策更优,我们看要满足什么条件

合并同类项化简之后会得到
嗯?这个式子怎么这么眼熟?这就是为什么我们叫他斜率优化

实现

是不是只要满足上面那个式子,决策就一定优于决策
那么我们把看做一个点,那么上面式子的左边可以看做点的斜率
而由于是单调不降的
所以我们的那个斜率要求单调递增(相等的话决策结果一样就不考虑了),并且大于等于对吧
这个可以用单调队列来维护
在队尾每次把点加入其中,条件是与队尾的点斜率大于队尾与队尾-1的点(维护斜率单调递增)
在队首每次查询,条件是队首的斜率满足要求(大于(同样相等不考虑了))
可能讲起来还很抽象,借助代码。。。
是按照上面的“斜率”定义来求斜率的函数
表示队列中的斜率
就是辣。。。定义一个结构体而已。。。

  1. int hd=1,tl=0;
  2. for(int i=1;i<=n;++i)
  3. {
  4. while(hd<tl&&k[tl-1]>=calc(i,Q[tl]))--tl;
  5. k[tl]=calc(i,Q[tl]),Q[++tl]=i;
  6. while(hd<tl&&k[hd]<ljl[i].w)++hd;
  7. dp[i]=dp[Q[hd]-1]+ljl[Q[hd]].h*ljl[i].w;
  8. }

汇总

可能需要结合整个代码和这个题来理解。。。
PS:如果还有不懂评论区留言吧。。。

  1. #include<bits/stdc++.h>
  2. #define lst long long
  3. #define ldb double
  4. #define N 50050
  5. using namespace std;
  6. const int Inf=1e9;
  7. int read()
  8. {
  9. int s=0,m=0;char ch=getchar();
  10. while(ch<'0'||ch>'9'){if(ch=='-')m=1;ch=getchar();}
  11. while(ch>='0'&&ch<='9')s=(s<<3)+(s<<1)+(ch^48),ch=getchar();
  12. return m?-s:s;
  13. }
  14. int n;
  15. ldb k[N];
  16. int Q[N];
  17. lst dp[N];
  18. struct Land{lst h,w;}ljl[N];
  19. bool cmp(const Land &a,const Land &b){return a.h==b.h?a.w>b.w:a.h>b.h;}
  20. ldb calc(int x,int y){return (dp[x-1]-dp[y-1])/(ljl[y].h-ljl[x].h);}
  21. int main()
  22. {
  23. n=read();
  24. for(int i=1;i<=n;++i)
  25. ljl[i]=(Land){read(),read()};
  26. sort(ljl+1,ljl+n+1,cmp);
  27. int L=1;
  28. for(int i=1;i<=n;++i)
  29. if(ljl[L].w<ljl[i].w)ljl[++L]=ljl[i];
  30. n=L;int hd=1,tl=0;
  31. for(int i=1;i<=n;++i)
  32. {
  33. while(hd<tl&&k[tl-1]>=calc(i,Q[tl]))--tl;
  34. k[tl]=calc(i,Q[tl]),Q[++tl]=i;
  35. while(hd<tl&&k[hd]<ljl[i].w)++hd;
  36. dp[i]=dp[Q[hd]-1]+ljl[Q[hd]].h*ljl[i].w;
  37. }printf("%lld\n",dp[n]);
  38. return 0;
  39. }
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注