zoukankan      html  css  js  c++  java
  • HDU3507 Print Article(斜率优化dp)

    前几天做多校,知道了这世界上存在dp的优化这样的说法,了解了四边形优化dp,所以今天顺带做一道典型的斜率优化,在百度打斜率优化dp,首先弹出来的就是下面这个网址:http://www.cnblogs.com/ka200812/archive/2012/08/03/2621345.html

    上面讲的很详细,但是实际上有些地方貌似是不小心写错了,所以我也来复述一下感悟一下收获。

    首先题意是比较明确的,如果我们定义dp[i]为打印到第i个字符时的最小花费的话,显然有下面的转移:

    dp[i]=dp[j]+(sum[i]-sum[j])^2+m

    然后我们考虑k<j<i时的情况,如果说我们求dp[i]的时候选择j比选择k更优,就会有:

    dp[j]+(sum[i]-sum[j])^2+m>dp[k]+(sum[i]-sum[k])^2+m

    然后化简就会有:

    (dp[j]+sum[j]^2)-(dp[k]-sum[k]^2)/2*(sum[j]-sum[k]) < sum[i] 

    然后左边的项看上去是非常像斜率的,所以我们可以定义一个g[j,k]=(dp[j]+sum[j]^2)-(dp[k]-sum[k]^2)/2*(sum[j]-sum[k])

    g[j,k]<sum[i]就表示了j点比k点更优。

    考虑a<b<c的情况  g[c,b]<g[b,a]的时候说明什么了呢??

    如果g[c,b]<sum[c]  那么 c比b更优

    如果g[c,b]>=sum[c],则g[b,a]>g[c,b]>=sum[c] 说明 a比b更优

    所以如果(c,b)的斜率<(b,a)的斜率那么 b点是不用考虑的。

    基于这一点我们可以用一个队列,像求凸包一样去维护可行的点集。由于g[c,b]<g[b,a]的情况不存在,所以只有g[c,b]>=g[b,a],因此点集应该是上凸的

    所以对于队列一开始的点集,如果队首元素的斜率g(que[qh+1],que[qh])<sum[i]的话,那么我们可以将队首元素弹出

    (其实一般情况下应该是不可以的,但由于sum[i]是递增的,所以在求dp[i]时有g(que[qh+1],que[qh])<sum[i],dp[i+1]也有g(que[qh+1],que[qh])<sum[i+1],因此队首的元素一定是不用考虑的)

    然后直到我们找到一个可行解。

    然后就将现在的元素插入队尾,插入队尾的时候就要维护下凸的性质,大概就是这样子。

    原博客讲的十分的清楚,然后评论里也讲了一些存在的纰漏和补充的地方,原博的下凸应该改为上凸吧,还有就是弹队首的时候还是要多加说明可能会更好。

    #pragma warning(disable:4996)
    #include <iostream>
    #include <cstdio>
    #include <vector>
    #include <algorithm>
    #include <cstring>
    #include <string>
    #include <cmath>
    using namespace std;
    
    #define ll long long
    #define maxn 510000
    
    ll dp[maxn];
    ll sum[maxn];
    ll a[maxn];
    int n, m;
    
    int que[maxn];
    int qh, qt;
    
    ll getup(int i, int j){
    	return (dp[i] + sum[i] * sum[i]) - (dp[j] + sum[j] * sum[j]);
    }
    
    ll getdown(int i, int j){
    	return 2 * (sum[i] - sum[j]);
    }
    
    int main()
    {
    	while (cin >> n >> m){
    		a[0] = sum[0] = 0;
    		for (int i = 1; i <= n; ++i){
    			scanf("%I64d", a + i);
    			sum[i] = sum[i - 1] + a[i];
    		}
    		dp[0] = 0;
    		qh = qt = 0;
    		que[qt++] = 0;
    
    		for (int i = 1; i <= n; ++i){
    			while (qh + 1 < qt && getup(que[qh + 1], que[qh]) <= getdown(que[qh + 1], que[qh]) * sum[i]){
    				qh++;
    			}
    			dp[i] = dp[que[qh]] + (sum[i] - sum[que[qh]])*(sum[i] - sum[que[qh]]) + m;
    			while (qh + 1 < qt && getup(i, que[qt - 1])*getdown(que[qt - 1], que[qt - 2]) <= getup(que[qt - 1], que[qt - 2])*getdown(i, que[qt - 1])){
    				qt--;
    			}
    			que[qt++] = i;
    		}
    		printf("%I64d
    ", dp[n]);
    	}
    	return 0;
    }
    
  • 相关阅读:
    软件工程第九周总结
    作为使用者对qq拼音输入法和搜狗输入法的评价
    关于编写Windows程序中启动兼容性问题
    软件工程第八周总结
    Java实验--关于课上找“水王”问题分析
    大道至简阅读笔记03
    家庭记账本-----一
    《人月神话》读后感----一到三章
    Java实现数据库与eclipse的连接
    流和文件
  • 原文地址:https://www.cnblogs.com/chanme/p/3891049.html
Copyright © 2011-2022 走看看