OI补完计划——Day2斜率优化
Day2
{前一天在火车上晃荡了一下午,今天来补上吧,昨天到省实验发现学生宿舍里没有床铺,连个垫子也没有。我们无奈去附近的批发市场买来了凉席和夏凉被,可是床板的硬度真不敢恭维。。。}
今天补上前天的欠账。斜率优化。。。
动态规划是一种非常重要的思想,它的重点在于合理的表示出问题中的状态,不重不漏,让问题变得合理有序,状态之间并可以高效的转移,从而方便地解决问题。
然而,一个方程在转移过程中可能会做许多无用功。这时我们就要合理的优化状态转移过程,舍弃那些一定不为最优解的决策,是动态规划得到优化。而怎样判断什么一定不是最优决策呢?这是我们就要去探求决定决策一与决策二优劣程度的内在条件,有时他是和状态无关的(当前状态一旦确定,那么两个决策的优劣程度就确定了)。看起来括号里的是废话,但如果我们将上一个状态的决策优劣程度应用于当前的决策,我们就可以,不必循环所有决策就可以找到最优决策,当然这是某些情况下。
斜率优化便是利用这样的思想,巧妙的利用斜率的大小将决策的优劣程度直观的展现出来,使问题的时间复杂度大幅下降,甚至到达问题的下界。这是一个经典的思想,直观的表示,高效的转移,极低的编程复杂度,使它成为优秀的算法。
{又罗嗦了一堆,下面今日正题。。。}
先来看一个例题:
【问题描述】
N个任务排成一个序列在一台机器上等待完成(顺序不得改变),这N个任务被分成若干批,每批包含相邻的若干任务。从时刻0开始,这些任务被分批加工,第i个任务单独完成所需的时间是Ti。在每批任务开始前,机器需要启动时间S,而完成这批任务所需的时间是各个任务需要时间的总和(同一批任务将在同一时刻完成)。每个任务的费用是它的完成时刻乘以一个费用系数Fi。请确定一个分组方案,使得总费用最小。
例如:S=1;T={1,3,4,2,1};F={3,2,3,3,4}。如果分组方案是{1,2}、{3}、{4,5},则完成时间分别为{5,5,10,14,14},费用C={15,10,30,42,56},总费用就是153。
【输入格式】
第一行是N(1<=N<=5000)。
第二行是S(0<=S<=50)。
下面N行每行有一对数,分别为Ti和Fi,均为不大于100的正整数,表示第i个任务单独完成所需的时间是Ti及其费用系数Fi。
【输出格式】
一个数,最小的总费用。
【输入样例】batch.in
5
1
1 3
3 2
4 3
2 3
1 4
【输出样例】batch.out
153
{话说当时我连N^2的算法也没有想到,惭愧啊}
直接上方程吧。。。
Start表示启动时间,sumT[i]表示前i个任务的时间和,sumF[i]表示i到n的费用和。
考虑对于确定状态i来说的,a和b两个决策。
假设决策a优于决策b,即:
变形得:
观察发现关于决策的几项被放在了同一边,那么便符合了上文提到的”决策优劣的无关性’’。此时考虑表达式的形式,有些与斜率形似。那么是否可以考虑使用数形结合呢?
将决策排成一排(如图中的节点),那么相邻两个节点间的连线的斜率便可以表示,关系始终的左侧。而右侧是关于i的表达式,他是判断状态i时两决策优劣的标准,即用斜率的之和标准比较即可得到优劣关系,而决策间的斜率是和状态无关的,以下称之为筛选斜率。
那么对于斜率不断增大的一排点来说,若队头两点的斜率大于筛选斜率,那么对优劣性来说,前一个决策均优于后一个决策。那么我们就可以直接选第一个决策作为最优决策。可是实际上我们将1到i-1的所有决策均表示在坐标系中时,斜率并不是单调递增的。那么,如何选出最优决策呢?
我们发现导致斜率非单调递增的节点就是形如上图中的红色节点。他显然不在所有决策的下凸壳上,而我们可以证明的是,’’这样’’的点当筛选斜率为任何值时,其均不为最优决策。
方法是通过分类讨论,读者可以自己尝试证明一下。
这样我们就获得了一个决策的下凸壳,可以方便的选出决策。我们现由最简单的情况引入,若题目中决策点的X坐标是随状态单调递增的,且筛选斜率也为单调递增的。就像本题中的sumF(X坐标),start+sumT(筛选斜率)。那么,我们可以直接在队头删除斜率小于筛选斜率的节点(这些决策是劣于后一个决策的),知道筛选斜率小于队头斜率,那么,队头便为最优决策。每次在队尾加入决策(因为决策X坐标单调),删除不在凸壳上的点即可。这样通过一个双端队列,维护状态i的最优决策,时间复杂度为O(N)。
再考虑第二种情况,若筛选斜率不为单调怎么办?我们可以发现,我们无法再删除队头结点了,因为之前删除的节点可能因为筛选斜率的减小再一次成为最优决策。那么考虑,什么样的节点是最优的,即它和前面的节点的斜率小于筛选斜率,而和后面节点的斜率大于筛选斜率,显然其为最优决策。而斜率是单调的,我们可以采用二分法,时间复杂度为O(NlogN)。
还有一种比较坑爹的情况,那就是决策点的X坐标不是单调的,那么意味着我们在增加决策的时候,不一定在队尾增加。这是我们只能动态的维护一个凸包,这是极其麻烦的,用到平衡树来维护,想法是比较简单的,就是实现无限悲剧啊。
【欢迎大家指出文中的不足,我们再作进一步的讨论,如有错误我会及时修改】
【Snow Dancer原创,转载请注明,谢谢】