zoukankan      html  css  js  c++  java
  • P2365 任务安排 斜率优化dp入门(分组dp)

    P2365 任务安排 斜率优化入门

    题意

    给出n个任务,必须按顺序完成,每个任务都有一个需要的时间和代价系数。可以把任务分批(就是把原序列分成多段),每一批内部的任务同样也是按顺序完成,但是最后计算代价的时候是该批中最后一个任务完成得时间*批中每一个任务得代价系数。同时,分批有一个代价时间S,表示启动机器的时间,也就是每分一批要时间S。

    思路

    刚开始没看懂题,感觉全部弄到一批就行了,其实不对,例如有两个任务,第一个时间为1,第二个时间为100000,代价均为一个比较大的值,S为1,那么肯定第一和第二分开代价比较小。
    n<=5000 考虑一下怎么dp
    基本套路是前i个分成j个的代价,这样的复杂度是((n^3))肯定是过不了的
    造成这个代价的最主要原因是记录了分成几组,如果不记录会造成后效性。实际上,我们可以考虑,假设分组增加了1,那么造成的代价是可以O(1)计算出来的,例如 设j<=i 如果我们将j---i分成一组,那么也就相当于 j----n的时间都要增加S,也就是增加了代价s*(sum[n]-sum[j-1]),这样就能避免后效性了,相当于我可以提前计算分组造成的代价,这个代价是分组必然造成的,而对后面的任务没有影响,假如在这个分组后又有分组,也是可以提前计算,这样就可以把复杂度优化到(O(n^2))
    我们设(dp[i])表示1--i都完成了需要的最小代价,那么转移方程为
    (dp[i]=min(dp[j-1]+sumt[i]*(sumf[i]-sumf[j-1])+s*(sumf[n]-sumf[j-1])))

    其实这题还可以更优化,我们把上述转移方程拿出来
    (dp[i]=dp[j-1]+sumt[i]*(sumf[i]-sumf[j-1])+s*(sumf[n]-sumf[j-1]))

    移动一下
    (dp[i]=dp[j-1]+sumt[i]*(sumf[i]-sumf[j-1])+s*(sumf[n]-sumf[j-1]))

    (dp[j-1]=dp[i]-sumt[i]*(sumf[i]-sumf[j-1])-s*(sumf[n]-sumf[j-1]))
    化简一下
    (dp[j-1]=(s+sumt[i])*sumf[j-1]+dp[i]-sumt[i]*sumf[i]-s*sumf[n])
    把dp[j-1]当成因变量,sumf[j-1]看作自变量,那么截距就是(dp[i]-sumt[i]*sumf[i])要使dp[i]最小也就是使截距最小

    可以看出斜率k=(s+sumt[i])是单调递增的
    找到dp[i]的最大值就当于把斜率为k的直线向上平移,在碰到的第一个(dp[j-1],sumt[j-1])点取得最小值。

    什么时候能碰到,假设碰到的点为x1 那么设(x1,x0)斜率为k0和(x1,x2)斜率为k1 那么取得最小值的斜率关系有(k0=<k=<k1)所以只要维护一个下凸壳就行,每次取能取第一个大于斜率(s+sumf[i])点(当点不足得时候只能取那一个点) ,并且维护一个下凸壳即可

    (n^2)代码

    #include<bits/stdc++.h>
    using namespace std;
    #define pb push_back
    #define F first
    #define S second
    #define mkp make_pair
    #define pii pair<int,int>
    typedef long long ll;
    const int inf=0x3f3f3f3f;
    const int maxn=1e6+10;
    const int mod=1e9+7;
    int dp[maxn],sumt[maxn],sumf[maxn];
    int main(){
    	int n,s;
    	scanf("%d%d",&n,&s);
    	for(int i=1;i<=n;i++){
    		int x,y;scanf("%d%d",&x,&y);
    		sumt[i]=sumt[i-1]+x;
    		sumf[i]=sumf[i-1]+y;
    	}
    	for(int i=1;i<=n;i++)dp[i]=inf;
    	dp[0]=0;
    	for(int i=1;i<=n;i++){
    		for(int j=1;j<=i;j++){
    			dp[i]=min(dp[i],dp[j-1]+sumt[i]*(sumf[i]-sumf[j-1])+s*(sumf[n]-sumf[j-1]));
    		}
    	}
    	cout<<dp[n];
    	return 0;
    }
    

    斜率优化代码

    
    #include<bits/stdc++.h>
    using namespace std;
    #define pb push_back
    #define F first
    #define S second
    #define mkp make_pair
    #define pii pair<int,int>
    typedef long long ll;
    const int inf=0x3f3f3f3f;
    const int maxn=1e6+10;
    const int mod=1e9+7;
    int dp[maxn],sumt[maxn],sumf[maxn],q[maxn];
    int main(){
    	int n,s;
    	scanf("%d%d",&n,&s);
    	for(int i=1;i<=n;i++){
    		int x,y;scanf("%d%d",&x,&y);
    		sumt[i]=sumt[i-1]+x;
    		sumf[i]=sumf[i-1]+y;
    	}
    	for(int i=1;i<=n;i++)dp[i]=inf;
    	dp[0]=0;
    	int l,r=1;
    	r=l=1;
    	for(int i=1;i<=n;i++){
    		while(l<r&&(dp[q[l+1]]-dp[q[l]])<=(s+sumt[i])*(sumf[q[l+1]]-sumf[q[l]]))l++;
    		dp[i]=dp[q[l]]+sumt[i]*(sumf[i]-sumf[q[l]])+s*(sumf[n]-sumf[q[l]]);
    		while(l<r&&(dp[q[r]]-dp[q[r-1]])*(sumf[i]-sumf[q[r]])>=(dp[i]-dp[q[r]])*(sumf[q[r]]-sumf[q[r-1]]))r--;
    		q[++r]=i;
    	}
    	cout<<dp[n];
    	return 0;
    }
    
  • 相关阅读:
    使用git将本地代码上传到gitee【码云】
    .net设置实例字段在请求参数中不展示
    Springboot 操作Elasticsearch 方式一 【spring-data-elasticsearch】
    Java工具类 (3)------>WordUtils------>利用Poi根据模板生成新的word文档
    Java工具类 (2)------>TreeUtils------>树形结构生成类
    Java工具类 (1)------>IPUtils------>获取用户登录IP地址
    Idea安装MyBatisCodeHelper-Pro插件破解版以及去除mybatis的mapper.xml文件背景颜色
    Python 线程队列 LifoQueue – LIFO
    Python 线程队列 Queue – FIFO
    Python 线程障碍对象 Barrier
  • 原文地址:https://www.cnblogs.com/ttttttttrx/p/12650507.html
Copyright © 2011-2022 走看看