Machine Works
Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others)Total Submission(s): 1004 Accepted Submission(s): 298
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.
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.
Follow the format of the sample output.
题意:
给你n个机器,每一个机器人只在第Di天销售,价格为Pi,二手转卖的价格是Ri,每天可以盈利Gi
一开始你有C的资金,问你在第D+1天你手上最大的资金是多少(在第D+1将会把手上的机器自动卖掉,并且你只能拥有一台机器,在把机器买掉和卖掉的当天机器不工作)
解析:
这篇博客写的很详细。
当你化简式子之后,就可以得到这样一个式子 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;
}