本文纯属原创,转载请注明出处。http://blog.youkuaiyun.com/zip_fan,谢谢。
题目传送门:http://acm.hdu.edu.cn/showproblem.php?pid=5380。
Travel with candy
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 262144/262144 K (Java/Others)Now, Mph want you to calculate the minimum cost in his travel plan.
The first line has a number T, representing the number of test cases.
For each test :
The first line contains two numbers N and C (N≤2×105,C≤106)
The second line contains N numbers a1,a2,...,an . It is guaranteed that ai>ai−1 for each 1<i<=N .
Next N+1 line : the i-th line contains two numbers buyi−1 and selli−1 respectively. ( selli−1≤buyi−1≤106 )
The sum of N in each test is less than 3×105 .
1 4 9 6 7 13 18 10 7 8 4 3 2 5 4 5 4
105
出题人保证了m大于等于任意两个相邻的城市之间的距离。
思路:
1、在路上消耗的糖果一定是尽量最便宜的。
2、其它的部分我们可以带着准备卖。
那么每次离开一个点的时候,我们都把口袋补充满。
那么每次到达一个点的时候,我们口袋里都会剩下路途消耗之后的糖果。
此时:
1、口袋里那些购买价格高于当前点购买价格的糖果,我们可以当它们没有被买过,直接以买入价卖出就好。
2、口袋里那些购买价格低于当前点卖出价格的糖果,我们可以当它们有3种用途,第一种是后面被优先消耗了,第二种是在价格更高的点卖出了,第三种是到了最后剩下了。前两种可以忽视掉当前点,第三种则相当于这些糖果在这个点卖出了。那么我们就可以看做这些糖果在这个点就被卖出了,那么它们的买入价就可以看做这个点的卖出价。
或者换句话说,我买入了一个糖果,它的价值就是买入价,如果我带着它到了一个卖出价更高的地方,那么它的价值就成了卖出价。在路上我只消耗当前价值最低的糖果,如果一个糖果到了最后我都没有吃的话,那么就意味着我可以在它价值最高的地方把它卖掉。
下面贴代码。
#include <cstdio>
#include <cmath>
#include <cstring>
#include <string>
#include <map>
#include <queue>
#include <vector>
#include <iostream>
#include <algorithm>
#define moo 1000000007//10^9+7
#define PI acos(-1.0)
#define eps 1e-5
using namespace std;
struct City
{
int dis;
int buy;
int sel;
}a[200005];
struct Queue
{
int num;
int val;
}que[200005];
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
int n,m;
scanf("%d%d",&n,&m);
a[0].dis=a[1].dis=0;
for(int i=2;i<=n+1;i++)
scanf("%d",&a[i].dis);
for(int i=1;i<=n+1;i++)
scanf("%d%d",&a[i].buy,&a[i].sel);
//以1号点为起点,n+1号点为终点,0号点的距离置0是为了起点不用单独拿出来讨论,节约代码。
long long ans=0;
int he=0;
int en=0;//单调队列维护口袋里的糖果。
int sum=0;//存的当前口袋里的糖果总量
for(int i=1;i<=n+1;i++)
{
int dis=a[i].dis-a[i-1].dis;
sum-=dis;//每到一个点,首先消耗dis个
while(dis!=0)
{
if(que[he+1].num<=dis)
{
dis-=que[he+1].num;
he++;
}
else
{
que[he+1].num-=dis;
dis=0;
}
}//从队列头开始吃掉dis个糖果
//将口袋里价值低于当前点出售价格的糖果价值更新为当前点的出售价格
for(int j=he+1;j<=en;j++)
if(que[j].val<a[i].sel)
que[j].val=a[i].sel;
//将口袋里价值高于当前点购买价格的糖果按它们最高价值卖掉。
while(en>he&&que[en].val>a[i].buy)
{
ans-=(long long)que[en].val*que[en].num;
sum-=que[en].num;
en--;
}
//离开该点前,将口袋补充满
if(sum!=m)
{
en++;
que[en].num=m-sum;
que[en].val=a[i].buy;
ans+=(long long)que[en].num*que[en].val;
sum=m;
}
}
//将最后剩下的糖果按照它们的最高价值卖掉
while(he!=en)
{
ans-=(long long)que[he+1].num*que[he+1].val;
he++;
}
cout<<ans<<endl;
}
return 0;
}