hdu 3842 Machine Works
2016-07-25 21:15
316 查看
题目链接
机器在买入和卖出当天不可以运作,但是可以在同一天买入一台机器并卖出另一台机器。
厂房里只能有一台机器。
在n+1天时,你必须卖掉手头的机器。
。。。
还有一堆条件,详见题目。
首先推出dp方程:
定义dp[i]表示在第di天时卖掉手头的机器后的钱数。
那么可以知道
dp[i]=Max{dp[i−1],dp[j]−p[j]+r[j]+(d[i]−d[j])∗g[j]}
条件是dp[j]>p[j]
可以先假定A[i]=dp[i]−p[i]+r[i]
那么,对于两个数j,k如果j比k优,就有
A[j]+(d[i]−d[j])∗g[j]>A[k]+(d[i]−d[k])∗g[k]
通过移项,可以将关于i的限制条件换到一边,于是得到:
B[i]=A[i]−(D[i]+1)∗G[i]
−Di<Bk−BjGj−Gk
同时可推出新的dp方程B[j]=−d[i]∗g[j]+dp[i]。
可以将(g[j],B[j])看做平面上的点,答案就是求过一些点,且斜率为-d[i]的直线中,截距的最大值。
于是可以维护一个凸包,然后更新答案。
维护这个凸包要先按g排序,然后在维护,但是dp过程中不可能每次都sort一遍,所以这时候就需要cdq分治。
cdq分治,简而言之,就是首先递归计算左边一半的值,然后利用左边一半的值去更新右边一半。
于是问题就变得明朗了,每次先把左边一半递归计算,然后把左边一半维护成一个凸包,然后更新右边一半的dp值。
由于题目要求最后一天必须卖掉手头的机器,可以虚设一个n+1点,答案就是dp[n+1]。
同时可以虚设一个0结点作为开始。
题目大意
有一些机器,可以在di时将它买入,价格为pi,在一段时间后可以以ri的价格卖出,机器运作时每天收入为gi。机器在买入和卖出当天不可以运作,但是可以在同一天买入一台机器并卖出另一台机器。
厂房里只能有一台机器。
在n+1天时,你必须卖掉手头的机器。
。。。
还有一堆条件,详见题目。
题解
这是一道cdq分治优化dp的题目,和cash那题差不多。首先推出dp方程:
定义dp[i]表示在第di天时卖掉手头的机器后的钱数。
那么可以知道
dp[i]=Max{dp[i−1],dp[j]−p[j]+r[j]+(d[i]−d[j])∗g[j]}
条件是dp[j]>p[j]
可以先假定A[i]=dp[i]−p[i]+r[i]
那么,对于两个数j,k如果j比k优,就有
A[j]+(d[i]−d[j])∗g[j]>A[k]+(d[i]−d[k])∗g[k]
通过移项,可以将关于i的限制条件换到一边,于是得到:
B[i]=A[i]−(D[i]+1)∗G[i]
−Di<Bk−BjGj−Gk
同时可推出新的dp方程B[j]=−d[i]∗g[j]+dp[i]。
可以将(g[j],B[j])看做平面上的点,答案就是求过一些点,且斜率为-d[i]的直线中,截距的最大值。
于是可以维护一个凸包,然后更新答案。
维护这个凸包要先按g排序,然后在维护,但是dp过程中不可能每次都sort一遍,所以这时候就需要cdq分治。
cdq分治,简而言之,就是首先递归计算左边一半的值,然后利用左边一半的值去更新右边一半。
于是问题就变得明朗了,每次先把左边一半递归计算,然后把左边一半维护成一个凸包,然后更新右边一半的dp值。
由于题目要求最后一天必须卖掉手头的机器,可以虚设一个n+1点,答案就是dp[n+1]。
同时可以虚设一个0结点作为开始。
const int M=100005; #define fi first #define se second typedef long long ll; typedef pair<int,ll> pii; inline void rd(int&res){ res=0;char c; while(c=getchar(),c<48); do res=res*10+(c^48); while(c=getchar(),c>47); } struct node{ int d,p,r,g; //卖出时间,价格,折扣价.利润 void input(){ rd(d);rd(p);rd(r);rd(g); } }mac[M]; ll dp[M]; bool cmp(node a,node b){ return a.d<b.d; } inline ll calc(int x){ return dp[x]+mac[x].r-(ll)(mac[x].d+1)*mac[x].g-mac[x].p; } pii q[M],stk[M]; inline bool slope(pii a,pii b,pii c){ return (double)(a.se-b.se)*(c.fi-a.fi)<(double)(b.fi-a.fi)*(a.se-c.se); } void solve(int L,int R){ if(L==R) return; int mid=(L+R)>>1; solve(L,mid); int tot=0,top=0,j=0; for(int i=L;i<=mid;i++) if(dp[i]>=mac[i].p) q[tot++]=pii(mac[i].g,calc(i)); sort(q,q+tot); for(int i=0;i<tot;i++){ while(top>1&&!slope(stk[top-1],stk[top],q[i])) top--; stk[++top]=q[i]; } for(int i=mid+1;i<=R;i++){ ll b1,b2,a1,a2,x; x=mac[i].d; while(j<top){ a1=stk[j].fi;b1=stk[j].se; a2=stk[j+1].fi;b2=stk[j+1].se; if(b2+(ll)a2*x<=b1+(ll)a1*x) break; j++; } dp[i]=max(dp[i],stk[j].fi*x+stk[j].se); } solve(mid+1,R); } int main(){ int n,D,cas=0; while(scanf("%d%lld%d",&n,&dp[0],&D)!=EOF,n||dp[0]||D){ for(int i=1;i<=n;i++) mac[i].input(); sort(mac+1,mac+n+1,cmp); n++; mac .d=D+1; mac .g=mac .p=0; for(int i=1;i<=n;i++) dp[i]=dp[0]; solve(0,n); printf("Case %d: %lld\n",++cas,dp ); } return 0; }
相关文章推荐
- 详解Android应用中屏幕尺寸的获取及dp和px值的转换
- 基于Android中dp和px之间进行转换的实现代码
- Android中dip、dp、sp、pt和px的区别详解
- LFC1.0.0 版本发布
- Android dpi,dip,dp的概念以及屏幕适配
- Android px、dp、sp之间相互转换
- HP data protector软件学习1--基本角色与基本工作流程
- HP data protector软件学习2--软件组成与界面介绍
- android中像素单位dp、px、pt、sp的比较
- Android对px和dip进行尺寸转换的方法
- Android根据分辨率进行单位转换-(dp,sp转像素px)
- android 尺寸 dp,sp,px,dip,pt详解
- DP问题各种模型的状态转移方程
- POJ-1695-Magazine Delivery-dp
- nyoj-1216-整理图书-dp
- TYVJ1193 括号序列解题报告
- 对DP的一点感想
- TYVJ上一些DP的解题报告
- soj1005. Roll Playing Games
- 01背包问题