第十届蓝桥杯B组省赛灵能传输题解

第十届蓝桥杯B组省赛灵能传输题解

请添加图片描述
请添加图片描述
请添加图片描述

题目分析

问题转化

S k = ∑ i = 1 k a i S_k=\sum_{i=1}^k{a_i} Sk=i=1kai
选中某个"高阶圣堂武士" a i a_i ai的结果是 S i − 1 + a i , S i − a i S_{i-1}+a_i,S_i-a_i Si1+ai,Siai
s w a p ( S i , S i − 1 ) swap\left(S_i,S_{i-1}\right) swap(Si,Si1)
设原前缀序列为 S 0 , S 1 , S 2 , . . . , S n S_0,S_1,S_2,...,S_n S0,S1,S2,...,Sn
题目要转化为:
求序列 { t i } = t 1 , t 2 . . . , t n + 1 , 其 中 ( t 1 = 0 , t n + 1 = n ) \left\{t_i\right\}=t_1,t_2...,t_{n+1},其中(t_1=0,t_{n+1}=n) {ti}=t1,t2...,tn+1,(t1=0,tn+1=n)
使
f = max ⁡ ( ∣ S t ( i ) − S t ( i − 1 ) ∣ ) , i ∈ [ 1 , n + 1 ] f=\max\left(\left|S_{t_{(i)}}-S_{t_{(i-1)}}\right|\right),i\in\left[1,n+1\right] f=max(St(i)St(i1)),i[1,n+1]
取得最小值

算法设计

首先想到的是对 s i s_i si排序,因为排序后与 s i s_i si相邻的元素一定与 s i s_i si差的最少。但是最开始的0与最后的 s n s_n sn是不可能移动的,这是算法设计的主要障碍。
解决方案如图所示
在这里插入图片描述
从0与 S n S_n Sn中选取较小的作为 S L S_L SL,(也就是图中的B),然后以间隔2跳跃到最小值(图中的L).选大的中间至少会有间隔为3的情况(数数中间那条线与曲线的交点),很不合理,而选大的往最大值跳与选小的往最小值跳等价
最后一跳间隔可能是1,这取决于0在序列中的位置(考虑最小的是K而非L)
原理很简单,如果选择了路径BC那么也就选择了AD,而AD显然大于BD和CA,依次类推,选择间隔2跳跃是合理的。中间的连续访问即可,最右边也要跳跃访问,策略与左侧类似。
然后按照该方法在连续数列(1,2,3,…,n)中访问即可得到 { t i } \left\{t_i\right\} {ti}
例如给定L,R,N对应的序列为, t 0 t_0 t0未打印。
在这里插入图片描述
跳跃测试C++代码

#include<bits\stdc++.h>
#define LEFT 0
#define RIGHT 1
using namespace std;
int num,L,R,D,per;
int engin(int i){//跳跃遍历引擎 
	if(num<3)return num;
	if(D==LEFT){
		if(i<=L){//从L向1靠
			if(i!=1&&i!=2){//一切正常 
				return i-2;
			}
			else{//到头了 
				D=RIGHT;//改变方向 
				if(i==2)return 1;
				if(i==1&&R!=2)return 2;
				return 3;
			}
		}
		if(i>R){//从最右向R靠 
			if(i==R+1)return R;
			return i-2;
		}
	}else{
		if(i<L){//从左向lL
			if(i==1){
				if(per==3)return 2;
			}
			if(i+2!=R)return i+2;//不能提前跳到R上 
			if(i+2==R&&R<num)return i+3;//如果有空间可以考虑R的下一个 
			if(i+2==num&&i+2==R)return num;//如果就剩一个了那就结束 
			return i+2;
		} 
		if(i>L&&i<R){//从L到R 
			if(i<R-1)return i+1;
			if(i==R-1&&R<num)return i+2;//不提前访问 
			if(i==R-1&&R==num)return num;//除非是最后一个 
		} 
		if(i>R){//从R向最右 
			if(i!=num&&i!=num-1){//正常状态 
				return i+2;
			}else{//到头了 
				D=LEFT;//改变方向 
				if(i==num-1)return num;
				if(i==num)return num-1;
			} 
		} 
	}
}
int next(int i){
	int ret=engin(i);
	per=i;
	return ret;
}
int main(){
	while(1){
		cin >> L >> R >> num;
		int i=L;
		D=LEFT; 
		while(i!=R){ 
			i=next(i);
			cout << per << " ";
		}
		cout << i << endl;
	}
	return 0;
} 

完整C++代码
(由于输入规模太大,最后两组数据会擦边
有时100分有时92分,
加上ios::sync_with_stdio(false)后直接提速700ms!!!)
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

#include<bits\stdc++.h>
#define N 300010
#define LEFT 0
#define RIGHT 1
using namespace std;
int T,num,L,R,D,per;
long long w[N],*warrior=w+2;//方便整体搬移
int cmp(const void* a,const void* b){
	long long temp=*(long long*)a-*(long long*)b;
	if(temp>0)return 1;
	if(temp<0)return -1;
	return 0; 
}
int engin(int i){//跳跃遍历引擎 
	if(num<3)return num;
	if(D==LEFT){
		if(i<=L){//从L向1靠
			if(i!=1&&i!=2){//一切正常 
				return i-2;
			}
			else{//到头了 
				D=RIGHT;//改变方向 
				if(i==2)return 1;
				if(i==1&&R!=2)return 2;
				return 3;
			}
		}
		if(i>R){//从最右向R靠 
			if(i==R+1)return R;
			return i-2;
		}
	}else{
		if(i<L){//从左向lL
			if(i==1){
				if(per==3)return 2;
			}
			if(i+2!=R)return i+2;//不能提前跳到R上 
			if(i+2==R&&R<num)return i+3;//如果有空间可以考虑R的下一个 
			if(i+2==num&&i+2==R)return num;//如果就剩一个了那就结束 
			return i+2;
		} 
		if(i>L&&i<R){//从L到R 
			if(i<R-1)return i+1;
			if(i==R-1&&R<num)return i+2;//不提前访问 
			if(i==R-1&&R==num)return num;//除非是最后一个 
		} 
		if(i>R){//从R向最右 
			if(i!=num&&i!=num-1){//正常状态 
				return i+2;
			}else{//到头了 
				D=LEFT;//改变方向 
				if(i==num-1)return num;
				if(i==num)return num-1;
			} 
		} 
	}
}
int next(int i){//获取目标序列中下一个元素的下表并保存当前值
	int ret=engin(i);
	per=i;
	return ret;
}
int main(){
	ios::sync_with_stdio(false);//不加刚好卡在一秒,加了提速700ms
	cin >> T;
	while(T--){
		cin >> num;
		memset(w,0,sizeof(w));
		for(int i=1;i<=num;++i){//前缀和,包括0的
			cin >> warrior[i];
			warrior[i]+=warrior[i-1];
		}
		long long temp=warrior[num];  
		qsort(warrior,num+1,sizeof(long long),cmp);
		++num; 
		//所有元素右移O(1)时间复杂度
		//让元素下标从1开始是"跳跃遍历"引擎的要求!
		warrior=warrior-1;
		//拣择起点与终点
		for(int i=0;i<=num;++i){ 
			if(warrior[i]==0)L=i;
			if(warrior[i]==temp)R=i;
		}
		//先择小的为起点
		if(temp<0)swap(L,R); 
		int i=L;//从L开始
		long long dmax=0;
		D=LEFT; //先往最小值跳
		while(i!=R){ //访问到Sn就结束了
			i=next(i);
			dmax=max(dmax,abs(warrior[i]-warrior[per]));
		}
		dmax=max(dmax,abs(warrior[i]-warrior[per]));
		cout << dmax << endl;
		warrior=warrior+1;//记得搬回去,不然一会就不知道搬哪去了,
	}
	return 0;
} 
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值