zoukankan      html  css  js  c++  java
  • HDU3507 print article【斜率优化dp】

    打印文章

    时间限制:9000/3000 MS(Java / Others)内存限制:131072/65536 K(Java / Others)
    总共提交:14521已接受提交:4531


    问题描述
    零有一台旧的打印机,有时不能正常工作。由于它是古董,他仍然喜欢用它来打印文章。但是长时间工作太旧,肯定会磨损,所以用零成本来评价这个程度。
    有一天,零想要打印一个有N个单词的文章,而每个单词我有一个打印费用Ci。另外,零知道在一行中打印k个字将花费

    M是常数。
    现在,零想要知道最低成本,以完美安排文章。
     

    输入
    有很多测试用例。对于每个测试用例,第一行有两个数字N和M(0≤n≤500000,0≤M≤1000)。然后,在接下来的2到N + 1行中有N个数字。输入由EOF终止。
     

    产量
    一个数字,意思是打印文章的最低成本。
     

    示例输入
    5 5 9 7
     

    示例输出
    230
     

    作者

    Xnozero




    斜率优化dp

    我们以这道题为例

    我们由题很容易写出dp方程,设f[i]为打印前i个位置的最小代价,则f[i] = min{f[j] + (sum[i] - sum[j])^2} + M

    这样做是O(n^2)的复杂度

    这个时候就需要斜率优化了


    我们设k < j < i,若j比k优,则由状态转移方程有

    f[j] + (sum[i] - sum[j])^2 < f[k] + (sum[i] - sum[k])^2

    整理一下就是((f[j] + sum[j]^2) - (f[k] + sum[k]^2)) / (2 * sum[j] - 2 * sum[k]) < sum[i]

    我们令y1 = f[j] + sum[j]^2 ,y2 = f[k] + sum[k]^2,x1 =2 * sum[j] ,x2 = 2 * sum[k]

    那么就可以写成(y1 - y2)/(x1 - x2) < sum[i]

    也就是说当j,k代表的两个点的斜率满足这个不等式时,j恒比k优


    有什么用呢?

    我们设k < j <p,且K(p,j) < K(j,k),

    那么若K(p,j) < sum[i],则p比j优

    若K(p,j) >= sum[i],由于K(j,k) > K(p,j) > sum[i],则j不比k优,就是说k比j优

    即j一定没有贡献


    这样子我们只需维护点间的斜率单调【这题是递增】,对于每个f[i],我们只需找到最大的K<sum[i],那么该斜率对应直线的右端点就是我们要找的最优的转移

    怎么找最大的K呢?

    由于我们维护的是斜率单调,即K递增,我们可以二分

    特殊的,本题参照物sum是单调递增的,也就是说满足小于sum[i - 1]的一定满足小于sum[i],用一个单调队列维护就好了


    于是我们得到了本题的解


    一般地,对于状态转移方程dp[i] = dp[j] + f(i,j),若能转换成上述与斜率有关的不等式,都可以用斜率优化,O(n^2)转O(n),是不是很神奇?

    另外,若dp[i] = dp[j] + f(i),也就是说我们只需找最优的dp[j]与当前点无关,就可以用单调队列优化


    附上本题代码

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define LL long long int
    #define REP(i,n) for (int i = 1; i <= (n); i++)
    #define fo(i,x,y) for (int i = (x); i <= (y); i++)
    #define Redge(u) for (int k = head[u]; k != -1; k = edge[k].next)
    using namespace std;
    const int maxn = 500005,maxm = 100005,INF = 1000000000;
    inline int read(){
    	int out = 0,flag = 1;char c = getchar();
    	while (c < 48 || c > 57) {if (c == '-') flag = -1; c = getchar();}
    	while (c >= 48 && c <= 57) {out = out * 10 + c - 48; c = getchar();}
    	return out * flag;
    }
    int n,M,sum[maxn],q[maxn],f[maxn],head = 0,tail = 0;
    int getu(int u,int v){return (f[u] + sum[u] * sum[u]) - (f[v] + sum[v] * sum[v]);}
    int getd(int u,int v){return 2 * (sum[u] - sum[v]);}
    int getf(int i,int j){return f[j] + (sum[i] - sum[j]) * (sum[i] - sum[j]) + M;}
    int main()
    {
    	while (~scanf("%d%d",&n,&M)){
    		head = tail = 0;
    		REP(i,n) sum[i] = sum[i - 1] + read();
    		q[tail++] = 0;
    		for (int i = 1; i <= n; i++){
    			while (head + 1 < tail && getu(q[head + 1],q[head]) <= sum[i] * getd(q[head + 1],q[head]))
    				head++;
    			f[i] = getf(i,q[head]);
    			while (head + 1 < tail && getu(i,q[tail - 1]) * getd(q[tail - 1],q[tail - 2]) <= getu(q[tail - 1],q[tail - 2]) * getd(i,q[tail - 1]))
    				tail--;
    			q[tail++] = i;
    		}
    		printf("%d
    ",f[n]);
    	}
    	return 0;
    }
    


  • 相关阅读:
    kettle7.0数据库迁移(MySQL迁移到Postgresql,迁移过程中自动创建表结构)
    正向代理与反向代理区别
    MySQL存储引擎相关知识点
    设计模式-装饰器模式
    设计模式-策略模式
    算法—数据结构学习笔记(二)栈
    Spring Boot2.0学习笔记(一)
    关联容器——map
    迭代器
    C风格字符串
  • 原文地址:https://www.cnblogs.com/Mychael/p/8282826.html
Copyright © 2011-2022 走看看