Problem C: [noip2016十连测第五场]travel (贪心)

来源:互联网 发布:java static方法 编辑:程序博客网 时间:2024/06/05 10:14

Problem C: [noip2016十连测第五场]travel

Time Limit: 10 Sec  Memory Limit: 233 MB
Submit: 9  Solved: 3
[Submit][Status][Web Board]

Description

http://www.lydsy.com/JudgeOnline/upload/201610/statements(1).pdf
给定一个长度为 n 的格子序列 x1,x2,...,xn。每一次 Lyra 可以选择向左跳到任意一个还没到过的位置,也可以
向右跳到任意一个还没到过的位置。如果现在 Lyra 在格子 i,她下一步跳向格子 j,那么这次跳跃的花费为|xi-
xj|。 注意,跳意味着格子 i 和格子 j 中间其他的格子都不会被这次跳跃影响。并且, Lyra 不应该跳出边界。
Lyra 的初始位置在格子 s。 Lyra 将会在到访过所有格子恰好一次之后,在某个位置停下来,这样就完成了任务
。Lyra 想知道如果她一共向左跳了 L 次,那么她要完成任务的最小总花费是多少,并希望你输出任意一种花费最
小的方案。显然如果 Lyra 向左走了 L 次,那一定会向右走 n-L-l 次。特殊的, 如果 Lyra 没有办法完成任务
,请输出一行-1。

Input

第一行,三个整数 n, L, s,分别表示序列的大小,向左走的次数,和初始位置。
第二行, n 个数字,表示序列 xi。
n≤2*10^5, 0≤xi≤10^9。 x1<x2<...<xn-1<xn, 1≤s≤n, 0≤L≤n-1

Output

第一行,一个数字,表示答案,注意OJ测试只要输出这一个数字

Sample Input

3 1 21 2 3

Sample Output

3//Lyra 一开始在 2 的位置, 2->1->3 的路径中, Lyra 一共向左走了 1 次,花费为|2-1|+|1-3|=3。

HINT

[Submit][Status]

题解:贪心

当xi=i时,数列是等差的递增数列,每个的间距相同,所以我们肯定是考虑尽量的不走回头路,但是因为必须要向左走L步,所以至少有L段要被经过三次,那么我们从起点一直向左跳,跳到不能再跳为止,如果此时还是不够的话,我们就再跳到起点的右边正好多出剩下L步的为止,然后再向左跳,最后在向右跳即可。如果连续向左跳会多,那就考虑间隔的跳最终跳到头再转弯即可。

  -1的情况非常好处理,当 l=0但是s≠1的时候,是没有合法路径的。同理 l=n-1,s≠n也是没有合法路径的。
  先考虑特殊的情况,当 s=1的时候,显然答案的下界是 xn-x1,就是从最左边按次序一直跳到最右边,不过L>0的时候就不能这么做了。答案要求最小也就是说要尽量少走回头路。假如我们在 n这个位置停下,那么中间就需要走一些回头的路类来用掉 L,我们把所有i到i+1之间的区间看成一个线段。跳的路径相当于对线段进行覆盖。显然所有的线段都必须覆盖至少1次,而至少有 L个线段至少覆盖3次。下面给出证明。 
  假如某一次是从 i向左跳,那么之前一定会从左侧跳到i,肯定会覆盖[i-1,i]的线段,然后从i向左跳走回头路,又会覆盖[i-1,i]一次,最后因为一定要走到 n,所以在走完回头路后一定还会再向右跳回来,所以至少经过三次[i-1,i]。也就至少有L条线段被覆盖至少3次。这样我们就可以贪心的选取权值最小的L条线段计算作为答案了。显然这种情况下L越小,答案越优。

但是实际情况中,起点不一定是1,终点也不一定是n.我们假设终点t再起点s的左边,那么[1,s-1][t+1,n]中的点至少都需要经过两次,如果能在这两段中用掉较多的L,那我们中间剩下的L就会少,这样在好不过。以为中间[s,t]这一段的处理就相当于是上面[1,n]的处理,中间的线段都会被覆盖三次。那么两边最多会向左走n-t+s-1步,如果n-t+s-1>=L,那么中间的不需要向左走,直接一步一步的跳就好了。如果不够L步的话就考虑从中选取差值小的线段让其长度*3即可。然后根据对称原则,在计算终点在起点左边的情况,这时候其实就是向右跳n-L-1步,向上面一样处理即可。

然后我们就可以通过枚举终点,来更新答案。对于上面中起点相对位置的两种情况,我们单独看。每次就是在上一起点的基础上中间加入[i,i+1],然后[t+1,n]的长度减小[i,i+1],我们用小根堆维护,每次都尽可能利用小的。注意有可能存在无法满足答案的情况,就是堆中不够。

还需要注意的是中的段的头尾两条线段不能使用,因为无论怎么跳都没法满足,会影响两边的跳法。

#include<iostream>#include<cstdio>#include<algorithm>#include<cstring>#include<cmath>#include<queue>#include<vector>#define N 200003#define LL long long using namespace std;int a[N],c[N],n,s,m,vis[N];LL l[N],r[N];priority_queue<int,vector<LL>,greater<LL> > q;int main(){freopen("a.in","r",stdin);freopen("my.out","w",stdout);scanf("%d%d%d",&n,&m,&s);if (m==n-1&&s!=n||m==0&&s!=1) {printf("-1");return 0;}for (int i=1;i<=n;i++) scanf("%d",&a[i]);    for (int i=2;i<=n;i++) l[i]=(LL)(a[i]-a[i-1]);    for (int i=n-1;i>=1;i--) r[i]=(LL)(a[i+1]-a[i]);    LL sum=0; LL sum1=0; LL sum2=0; LL ans=(LL)1e9*(LL)1e9; int ansx=0;    LL sum3=0; LL last=0;    for (int i=1;i<s;i++) sum1+=(LL)r[i]*2;    for (int i=s;i<=n-1;i++) sum2+=(LL)r[i]*2;    for (int i=s+1;i<=n;i++)     {     int t=n-i+s-1;        sum+=(LL)l[i]; sum2-=(LL)l[i]*2;        if (t>=m) {        LL ans1=sum1+sum2+sum;        if (ans1<ans) ans=ans1,ansx=i;}else{if (q.empty()) break; LL x=q.top(); q.pop();sum3+=(LL)x*2;LL ans1=sum1+sum2+sum+sum3;if (ans1<ans) ans=ans1,ansx=i;} if (i!=s+1) q.push(l[i]); }int m1=n-m-1;sum1=0; sum2=0; sum3=0; sum=0;for (int i=s;i<=n-1;i++) sum1+=(LL)r[i]*2;for (int i=1;i<=s-1;i++) sum2+=(LL)r[i]*2;while (!q.empty()) q.pop();for (int i=s-1;i>=1;i--){int t=n-s+i-1;sum+=r[i]; sum2-=r[i]*2;if (t>=m1){LL ans1=sum1+sum2+sum;if (ans1<ans) ans=ans1,ansx=i;}else {    if (q.empty()) break;LL x=q.top(); q.pop();sum3+=(LL)x*2;LL ans1=sum1+sum2+sum+sum3;if (ans1<ans) ans=ans1,ansx=i;}if (i!=s-1) q.push(r[i]);}printf("%I64d\n",ans);}



0 0
原创粉丝点击