• [CSP-S模拟测试]:Travel(贪心+构造)


    题目描述

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


    输入格式

    第一行,三个整数$n,L,s$,分别表示序列的大小,向左走的次数,和初始位置。
    第二行,$n$个数字,表示序列$x_i$。


    输出格式

    第一行,一个数字,表示答案。
    如果能完成任务,则第二行,输出$n-1$个数字,表示方案。注意,$Lyra$初始的位置已经确定了,所以不要输出。


    样例

    样例输入:

    3 1 2
    1 2 3

    样例输出:

    3
    1 3


    数据范围与提示

    样例解释:

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

    数据范围:

    测试点$1sim 2,nleqslant 8,0leqslant x_i leqslant 10^9$。
    测试点$3sim 8,nleqslant 20,0leqslant x_i leqslant 10^9$。
    测试点$9sim 10,nleqslant 2 imes 10^5,x_i =i$。
    测试点$11sim 20,nleqslant 2 imes 10^5,0leqslant x_i leqslant 10^9$。
    对于所有数据,都满足$x_1<x_2<...<x_{n-1}<x_n,1leqslant sleqslant n,0leqslant Lleqslant n-1$。


    题解

    先来考虑$-1$的情况,分为两种情况。

      $alpha.L=0,s eq 0$,显然我们无论如何都不能走到$s$左边的点。

      $eta.L=n-1,s eq n$,则我们向右走的步数为$0$,其它类比上面。

    再来考虑一般情况。

    $x_1<x_2<...<x_{n-1}<x_n$是一个非常好的性质,也就是说,尽可能少的覆盖区间一定是最优的;这里所说的区间是指假如我从$l$走到了$r$,那么就将区间$[l,r]$覆盖了一遍,也就是付出了$x_r-x_l$的代价(假设$l$在左,$r$在右)。

    那么考虑如何选择这条路径。

    这时候我们分为一开始向右走和一开始向左走,显然这两种情况无非就是将区间$reverse$一下,那么我们现在只考虑向左走的情况。

    起点已经固定为$s$,不妨设终点为$t$,那么路径一定类似下图$downarrow$

    设在$s$左侧向左走了$l_1$步,在$t$右侧向右走了$l_2$步,那么可以通过调整在$s$左侧向左走的步数来满足$t$的右侧。

    如果都无法满足,则意味着我们需要在$ssim t$这段区间再向左走,如下图$downarrow$

    那么这样一定不是最优的,因为存在方案$ssim t'$一定比$ssim t$更优。

    于是可以枚举每一个$t$,去最小即可。

    路径直接构造就好了。

    一开始向右走的情况同理,两种方案取$min$即可。

    时间复杂度:$Theta(n)$。

    期望得分:$100$分。

    实际得分:$100$分。


    代码时刻

    #include<bits/stdc++.h>
    using namespace std;
    int n,l,s;
    int xl[200001],xr[200001],top;
    bool vis[200001];
    int ansl,ansr,quel[200001],quer[200001],pos[200001];
    pair<int,int> sta[200001];
    void solvel()
    {
    	if(l<s)
    	{
    		for(int i=s-1;i>=s-l+1;i--)quel[++quel[0]]=i;
    		for(int i=1;i<=s-l;i++)quel[++quel[0]]=i;
    		for(int i=s+1;i<=n;i++)quel[++quel[0]]=i;
    		ansl=xl[s]+xl[n]-(xl[1]<<1);
    		return;
    	}
    	if(l+1==n-1)
    	{
    		for(int i=s-1;i;i--)quel[++quel[0]]=i;
    		for(int i=n;i>s;i--)quel[++quel[0]]=i;
    		ansl=((xl[n]-xl[1])<<1)+xl[s]-xl[s+1];
    		return;
    	}
    	for(int i=s+2;i<n;i++)sta[++top]=make_pair(xl[i]-xl[i-1],i);
    	sort(sta+1,sta+top+1);
    	for(int i=1;i<=top;i++)pos[sta[i].second]=i;
    	int res=0;
    	for(int i=1;i<=l-s+1;i++)res+=sta[i].first;
    	int mx=res<<1,t=n,j=l-s+1;
    	for(int i=n-1,flag=l-s+1;i>=n-l+s-1;i--)
    	{
    		if(pos[i]<=flag)res-=sta[pos[i]].first;
    		else res-=sta[flag--].first;
    		while(flag&&i<=sta[flag].second)flag--;
    		if(mx>(res<<1)+xl[n]-xl[i]){mx=(res<<1)+xl[n]-xl[i];t=i;j=flag;}
    	}
    	for(int i=s-1;i;i--)quel[++quel[0]]=i;
    	for(int i=s+2;i<t;i++)if(pos[i]<=j)vis[i]=1;
    	for(int i=s+1;i<t;i++)
    	{
    		if(!vis[i+1])quel[++quel[0]]=i;
    		else
    		{
    			int flag=i+1;
    			while(vis[flag])flag++;
    			for(int j=flag-1;j>=i;j--)quel[++quel[0]]=j;
    			i=flag-1;
    		}
    	}
    	for(int i=n;i>=t;i--)quel[++quel[0]]=i;
    	ansl=xl[s]+xl[n]-2*xl[1]+mx;
    }
    void solver()
    {
    	top=0;
    	memset(vis,0,sizeof(vis));
    	if(l+2>s)
    	{
    		for(int i=n-s;i>=l-s+3;i--)quer[++quer[0]]=i;
    		for(int i=1;i<=l-s+2;i++)quer[++quer[0]]=i;
    		for(int i=n-s+2;i<=n;i++)quer[++quer[0]]=i;
    		ansr=xr[n-s+1]+xr[n]-(xr[1]<<1);
    		return;
    	}
    	if(l==1)
    	{
    		for(int i=n-s;i;i--)quer[++quer[0]]=i;
    		for(int i=n;i>n-s+1;i--)quer[++quer[0]]=i;
    		ansr=((xr[n]-xr[1])<<1)+xr[n-s+1]-xr[n-s+2];
    		return;
    	}
    	for(int i=n-s+3;i<n;i++)sta[++top]=make_pair(xr[i]-xr[i-1],i);
    	sort(sta+1,sta+top+1);
    	for(int i=1;i<=top;i++)pos[sta[i].second]=i;
    	int res=0;
    	for(int i=1;i<=s-l-1;i++)res+=sta[i].first;
    	int mx=res<<1,t=n,j=s-l-1;
    	for(int i=n-1,flag=s-l-1;i>=l-s+n+1;i--)
    	{
    		if(pos[i]<=flag)res-=sta[pos[i]].first;
    		else res-=sta[flag--].first;
    		while(flag&&i<=sta[flag].second)flag--;
    		if(mx>(res<<1)+xr[n]-xr[i]){mx=(res<<1)+xr[n]-xr[i];t=i;j=flag;}
    	}
    	for(int i=n-s;i;i--)quer[++quer[0]]=i;
    	for(int i=n-s+3;i<t;i++)if(pos[i]<=j)vis[i]=1;
    	for(int i=n-s+2;i<t;i++)
    	{
    		if(!vis[i+1])quer[++quer[0]]=i;
    		else
    		{
    			int flag=i+1;
    			while(vis[flag])flag++;
    			for(int j=flag-1;j>=i;j--)quer[++quer[0]]=j;
    			i=flag-1;
    		}
    	}
    	for(int i=n;i>=t;i--)quer[++quer[0]]=i;
    	ansr=xr[n-s+1]+xr[n]-2*xr[1]+mx;
    }
    int main()
    {
    	scanf("%d%d%d",&n,&l,&s);
    	for(int i=1;i<=n;i++)
    	{
    		scanf("%d",&xl[i]);
    		xr[n-i+1]=-xl[i];
    	}
    	if((!l&&s!=1)||(l==n-1&&s!=n)){puts("-1");return 0;}
    	solvel();solver();
    	if(ansl<ansr)
    	{
    		printf("%d
    ",ansl);
    		for(int i=1;i<n;i++)printf("%d ",quel[i]);
    	}
    	else
    	{
    		printf("%d
    ",ansr);
    		for(int i=1;i<n;i++)printf("%d ",n-quer[i]+1);
    	}
    	return 0;
    }
    

    rp++

  • 相关阅读:
    排序算法(牢记)
    【性能优化】优化笔记之一:图像RGB与YUV转换优化
    wikioi 3027 线段覆盖 2
    浅谈HTTP响应拆分攻击
    HTTP Response Spliting 防范策略研究
    百度地图API使用介绍
    百度地图
    PHP htmlspecialchars() 函数
    CSRF防范策略研究
    SQL手工注入
  • 原文地址:https://www.cnblogs.com/wzc521/p/11649240.html
Copyright © 2020-2023  润新知