• HDU2829 Lawrence(斜率优化dp)


    学了模板题之后上网搜下斜率优化dp的题目,然后就看到这道题,知道是斜率dp之后有思路就可以自己做不出来,要是不事先知道的话那就说不定了。

    题意:给你n个数,一开始n个数相邻的数之间是被东西连着的,对于连着的一片的数,它们的价值就是两两乘积的和。所以4 5 1 2一开始就是4*5+4*1+4*2+5*1+5*2+1*2... 注意到两两乘积的和其实是可以这么算的((a1+a2+a3+..an)^2-(a1^2+a2^2+....))/2。现在我可以在数与数之间切m刀,问切完之后的最小价值是多少。

    一个自然的想法是定义dp[n][t]表示的是前n个数被切了t刀的最小价值,不难发现方程是可以这么转移的

    dp[i][t]=dp[j][t-1]+((sum[i]-sum[j])*(sum[i]-sum[j])-(d[i]-d[j]))/2  (j<i) 其中d[i]表示前i个数的平方的和

    所以这个方程是1个三维的dp,对于1000的数据量是行不通的,所以仿照斜率优化的思路,我们可以尝试写出当 k<j时,j比k更优的方程,即:

    dp[j][t-1]+((sum[i]-sum[j])*(sum[i]-sum[j])-(d[i]-d[j]))/2 < dp[k][t-1]+((sum[i]-sum[k])*(sum[i]-sum[k])-(d[i]-d[k]))/2

    经过化简,我们可以得到一个这样的东西:

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

    显然又是一个斜率式子,利用先前推导的性质我们可以知道,我们每次更新的时候其实就是利用t-1层的信息推出t层的信息,推的时候实际有效的值也是一个下凸的点集,这个和上一题不一样之处就在于要更新m次,每次队列都要重新入队,但总体来说还是一样的,由于斜率优化后里层的dp达到线性,所以最后复杂度出来是O(n^2)的,仍然要注意之所以一开始可以弹队首是因为 sum[i]是递增的。

    #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 1100
    
    ll dp[maxn][maxn];
    ll a[maxn];
    ll sum[maxn];
    ll d[maxn];
    
    int n, m;
    
    ll getup(int i, int j, int t){
    	return (2 * dp[i][t] + sum[i] * sum[i] + d[i]) - (2 * dp[j][t] + sum[j] * sum[j] + d[j]);
    }
    
    ll getdown(int i, int j){
    	return 2 * (sum[i] - sum[j]);
    }
    
    int que[maxn];
    int qh, qt;
    
    int main()
    {
    	while (cin >> n >> m&&(n||m)){
    		a[0] = d[0] = sum[0] = 0;
    		for (int i = 1; i <= n; ++i){
    			scanf("%I64d", &a[i]);
    			sum[i] = sum[i - 1] + a[i];
    			d[i] = d[i - 1] + a[i] * a[i];
    		}
    		dp[0][0] = 0;
    		for (int i = 1; i <= n; ++i){
    			dp[i][0] = dp[i - 1][0] + a[i] * sum[i - 1];
    		}
    		for (int x = 1; x <= m; ++x){
    			dp[0][x] = 0;
    			qh = qt = 0;
    			que[qt++] = 0;
    			for (int i = 1; i <= n; ++i){
    				while (qh + 1 < qt && getup(que[qh + 1], que[qh], x - 1) <= sum[i] * getdown(que[qh + 1], que[qh])){
    					qh++;
    				}
    				dp[i][x] = dp[que[qh]][x - 1] + ((sum[i] - sum[que[qh]])*(sum[i] - sum[que[qh]]) - (d[i] - d[que[qh]])) / 2;
    				while (qh + 1 < qt && getup(i, que[qt - 1], x - 1)*getdown(que[qt - 1], que[qt - 2]) <= getup(que[qt - 1], que[qt - 2], x - 1)*getdown(i, que[qt - 1])){
    					qt--;
    				}
    				que[qt++] = i;
    			}
    		}
    		printf("%I64d
    ", dp[n][m]);
    	}
    	return 0;
    }
    
  • 相关阅读:
    LDAP入门
    Java程序员书籍推荐
    docker registry镜像容器时区时间同步
    微服务架构与实践及云原生等相关概念
    ts基础
    内网穿透连接内网下的"我的世界"服务端
    [量子互联] 内网穿透远程连接Linux的SSH
    树莓派开启SSH的N种方法
    [量子互联] 群晖NAS的远程映射配置
    [量子互联] 群晖NAS的qBittorrent端口映射
  • 原文地址:https://www.cnblogs.com/chanme/p/3891197.html
Copyright © 2020-2023  润新知