HDU 3842 Machine Works (CDQ分治+斜率优化DP)

Machine Works

Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 1004    Accepted Submission(s): 298


Problem Description
You are the director of Arbitrarily Complex Machines (ACM for short), a company producing advanced machinery using even more advanced machinery. The old production machinery has broken down, so you need to buy new production machines for the company. Your goal is to make as much money as possible during the restructuring period. During this period you will be able to buy and sell machines and operate them for profit while ACM owns them. Due to space restrictions, ACM can own at most one machine at a time.

During the restructuring period, there will be several machines for sale. Being an expert in the advanced machines market, you already know the price P i  and the availability day Di for each machines M i. Note that if you do not buy machine M i  on day Di, then somebody else will buy it and it will not be available later. Needless to say, you cannot buy a machine if ACM has less money than the price of the machine.

If you buy a machine M i  on day D i, then ACM can operate it starting on day D i  + 1. Each day that the machine operates, it produces a profit of Gi dollars for the company.

You may decide to sell a machine to reclaim a part of its purchase price any day after you’ve bought it. Each machine has a resale price R i  for which it may be resold to the market. You cannot operate a machine on the day that you sell it, but you may sell a machine and use the proceeds to buy a new machine on the same day.

Once the restructuring period ends, ACM will sell any machine that it still owns. Your task is to maximize the amount of money that ACM makes during the restructuring.
 

Input
The input consists of several test cases. Each test case starts with a line containing three positive integers N, C, and D. N is the number of machines for sale (N <= 10^5), C is the number of dollars with which the company begins the restructuring (C <= 10^9), and D is the number of days that the restructuring lasts (D <= 10^9).

Each of the next N lines describes a single machine for sale. Each line contains four integers D i, P i, R i  and G i,
denoting (respectively) the day on which the machine is for sale, the dollar price for which it may be bought, the
dollar price for which it may be resold and the daily profit generated by operating the machine. These numbers satisfy
1 <= D i  <= D, 1 <= R i  < P i  <=10^9 and 1 <= G i  <= 10^9.

The last test case is followed by a line containing three zeros.
 

Output
For each test case, display its case number followed by the largest number of dollars that ACM can have at the end of day D + 1.
Follow the format of the sample output.
 

Sample Input
 
 
6 10 206 12 1 31 9 1 23 2 1 28 20 5 44 11 7 42 10 9 10 0 0
 
Sample Output
 
 
Case 1: 44
 

题意:

给你n个机器,每一个机器人只在第Di天销售,价格为Pi,二手转卖的价格是Ri,每天可以盈利Gi

一开始你有C的资金,问你在第D+1天你手上最大的资金是多少(在第D+1将会把手上的机器自动卖掉,并且你只能拥有一台机器,在把机器买掉和卖掉的当天机器不工作)


解析:

点击打开链接

这篇博客写的很详细。

我这里就主要说明要用CDQ的原因。
这两篇博客最后面有讲解这个原因

当你化简式子之后,就可以得到这样一个式子 h(j)-h(k)>D[i]*(gk-gj)  (这里的gk,gj是机器的利润P)

这里不能直接用斜率优化DP,因为你不知道gk,gj的大小,也就是说g(利润)是不单调的,这个直接影响了斜率优化DP里面的插入的操作,因为插入是通过斜率来插入删去不是最优解的点,这里斜率不存在的话,就无法进行优化,所以就需要将按照横坐标(利润),但这里一开始肯定是需要根据销售时间进行排序的,这样D[i]才能单调,所以这里有两个需要排序的地方,所以很容易就想到CDQ来解决三位偏序的问题(di,gi,dp[i]),先根据di排序,再在归并左右区间的时候,分别在左右区间把他们按照g来排序,在计算DP,就可以了。

这里面还有一点与普通的斜率优化DP,不一样的是在插入的过程中的斜率比较,并不是比较

K(i,j)与K(j,k)  (i>j>k)而是比较K(i,k)与K(j,k)  这是因为就像上面那幅图,当(2,1)插入队列的时候,比较K(C,B)与K(B,A)的时候,发现B是不需要弹出的(即可能是最优解),这个明显不符合,所以就需要比较K(C,A)与K(B,A),出现这种情况的原因可能是因为我们把他们按照横坐标排序,但他们横坐标不是严格递增的,会出现不上升的情况.

还要注意的一点是这里队列中一开始需要放一个(0,m)的点(m表示初始的资金),貌似这个不放进去,会对之后的结果有影响,这也是这里为什么CDQ的范围是从[0,n]的原因

最后需要注意的一点是两个long long相乘(结果爆long long)好像只能用double来存值 ,unsigned long long 都放不下


#include <bits/stdc++.h>
using namespace std;
typedef long long int lli;
typedef pair<int,lli> pil;

const int MAXN = 1e5 +10;

#define INF 0x3f3f3f3f

typedef struct node
{
    lli day;
    lli cost;
    lli sold;
    lli profit;
    operator <(const node& oth)
    {
        return day<oth.day;
    }
}node;

node xb[MAXN];
pil P[MAXN];

lli dp[MAXN];
lli q[MAXN],head,tail;
lli d;

lli n,m;

lli cal(int j)
{
    return dp[j]-(xb[j].day+1)*xb[j].profit-xb[j].cost+xb[j].sold;
}

int insertslope(int k,int j,int i)   
{
    lli xa=P[j].first-P[k].first;
    lli xb=P[i].first-P[k].first;
    lli ya=P[j].second-P[k].second;
    lli yb=P[i].second-P[k].second;
    double tmp = (double)xa * yb - (double)xb * ya;   //double范围是-1.7*10^-308~1.7*10^308,unsigned long long 太小了
    return tmp < 0;
}

bool cmpp(node a,node b)
{
    return a.profit<b.profit;
}

void CDQ(int l,int r)
{
    if(l>=r) return;
    int mid = (l+r)>>1;
    CDQ(l,mid);
    int cnt;
    cnt=0;
    head=tail=0;
    for(int i=l;i<=mid;i++)  //在当前的钱下满足的机器
    {
        if(dp[i]>=xb[i].cost)
            P[cnt++]=pil(xb[i].profit,cal(i));
    }
    sort(P,P+cnt);  //按横坐标排序
    for(int i=0;i<cnt;i++)   //将A内的点插入到队列中,左区间插入
    {
        while(head+1<tail&&!insertslope(q[tail-2],q[tail-1],i))tail--;
        q[tail++]=i;
    }

    for(int i=mid+1;i<=r;i++)  //右区间查询
    {
        //while(head+1<tail&&UP(q[head+1],q[head])>xb[i].day*DOWN(q[head],q[head+1]))
        while(head+1<tail&&P[q[head+1]].second-P[q[head]].second>xb[i].day*(P[q[head]].first-P[q[head+1]].first))
                head++;
        dp[i]=max(P[q[head]].second+xb[i].day*P[q[head]].first,dp[i]);
    }

    CDQ(mid+1,r);

}

int main()
{
    int t;
    int ncount=0;
    while(scanf("%lld%lld%lld",&d,&m,&n),n+m+d)
    {
        //scanf("%lld%lld",&n,&m);
        ncount++;
        for(int i=1;i<=d;i++)
        {
            scanf("%lld%lld%lld%lld",&xb[i].day,&xb[i].cost,&xb[i].sold,&xb[i].profit);
        }
        d++;
        xb[d].day=n+1;
        xb[d].cost=xb[d].profit=xb[d].sold=0;
        sort(xb+1,xb+1+d);
        for(int i=0;i<=d;i++) dp[i]=m;
        CDQ(0,d);

        printf("Case %d: %lld\n",ncount,dp[d]);
    }
    return 0;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值