您的位置:首页 > 其它

hdu 3842 Machine Works

2016-07-25 21:15 316 查看
题目链接

题目大意

有一些机器,可以在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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  cdq分治 dp 斜率优化