开车旅行,洛谷之提高历练地,倍增

本文介绍了一种解决驾车旅行中寻找最优路径的问题。通过排序、倍增等算法技巧,结合链表进行节点维护,实现了高效的路径查找。代码示例使用 C++ 实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

正文

      第二题:开车旅行

      这题贼难~。

      首先我们要知道当前点的最短路和次短路的长度和所跳到位置,这样我们用倍增才好解决。。。

      我们就想着把它排序,因为排完序之后他的绝对值的最小值一定存在于排完序队列中的上一个(last),上上个(lastest),下一个(next)和下下个(nextest)。(四个东西)

      那么我们又要保证当前的 这四个东西 都 存在于 当前点 的 东边。什么数据结构可以 很好地 满足 这种删点和访问左右端点操作呢?(splay前驱后继???)

      其实我们用链表就可以了。每次找出那四个东西,然后再比较他们与当前高度差的绝对值,优先选绝对值小的 和 高度小的,更新一下,然后用4个数组记录一下最小值,最小值所取到的位置,次小值以及它的位置。删除这个点即可,当然要考虑全面。

代码<看不懂的评论问我哦~~我尽量讲明白>

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;

int n;
int k;
struct node{
	long long h;
	int x;
	bool operator<(const node y)const{
		return h<y.h;
	}
}p[100010];
struct nodenew{
	long long h;
	int last,lastest,next,nextest;
}s[100010];
long long dmin[100010],cmin[100010];
int wh[100010],whc[100010];
long long f[100010][20];
struct bloo{
	long long a,b,tot;
}tot[100010][20];
long long a=0,b=0;

void go(int i){
	int next=s[i].next,nextest=s[i].nextest;
	int last=s[i].last,lastest=s[i].lastest;
	s[next].last=last;s[next].lastest=lastest;
	s[nextest].lastest=last;
	s[last].next=next;s[last].nextest=nextest;
	s[lastest].nextest=next;
}

void prepare(){
	sort(p+1,p+1+n);
	for(int i=1;i<=n;i++) {//用链表维护
		s[p[i].x].h=p[i].h;//s为原序列,h为高度
		if(i>=2) s[p[i].x].last=p[i-1].x;//原序列最近一个比它小的就是在排好的序列中的上一个(的原编号)。
		if(i>=3) s[p[i].x].lastest=p[i-2].x;//同理
		if(i+1<=n) s[p[i].x].next=p[i+1].x;
		if(i+2<=n) s[p[i].x].nextest=p[i+2].x;
	}
	for(int i=1;i<=n;i++){
		int nexta=s[i].next,nextesta=s[i].nextest;//先记录下来,方便操作。
		int lasta=s[i].last,lastesta=s[i].lastest;
		if(abs(s[lasta].h-s[i].h)<=abs(s[nexta].h-s[i].h)){//最短路可能在最近一个比我小的和最近一个比我大的。
			dmin[i]=abs(s[lasta].h-s[i].h);wh[i]=lasta;//记录下来
			if(abs(s[lastesta].h-s[i].h)<=abs(s[nexta].h-s[i].h)){cmin[i]=abs(s[lastesta].h-s[i].h);whc[i]=lastesta;}
			else{cmin[i]=abs(s[nexta].h-s[i].h);whc[i]=nexta;}//再来记录次小值
		}
		else {
			dmin[i]=abs(s[nexta].h-s[i].h);wh[i]=nexta;//记录最短路
			if(abs(s[lasta].h-s[i].h)<=abs(s[nextesta].h-s[i].h)){cmin[i]=abs(s[lasta].h-s[i].h);whc[i]=lasta;}
			else {cmin[i]=abs(s[nextesta].h-s[i].h);whc[i]=nextesta;}//记录次短路
		}
		go(i);//链表的维护,把这个点删去,因为后面的点不能往前走
	}	
}

void begina(){//预处理倍增
	for(int i=1;i<=n;i++){
		tot[i][0].tot=cmin[i]+dmin[whc[i]];//tot记录从i开始,走1轮的路程
		tot[i][0].a=cmin[i];//走一轮后a的路程
		tot[i][0].b=dmin[whc[i]];//走一轮后b的路程
		f[i][0]=wh[whc[i]];//走一轮后的位置
	}
	for(int j=1;j<=16;j++)
		for(int i=1;i<=n;i++){//不断维护f[i][j]
			tot[i][j].tot=tot[i][j-1].tot+tot[f[i][j-1]][j-1].tot;
			tot[i][j].a=tot[i][j-1].a+tot[f[i][j-1]][j-1].a;
			tot[i][j].b=tot[i][j-1].b+tot[f[i][j-1]][j-1].b;
			f[i][j]=f[f[i][j-1]][j-1];
		}
}

double solve(long long x,long long k){
	int now=x;
	a=0;b=0;
	for(int i=16;i>=0;i--){
		if(k-tot[now][i].tot>=0){//能走就拼命走
			k-=tot[now][i].tot;
			a+=tot[now][i].a;
			b+=tot[now][i].b;
			now=f[now][i];
		}
	}
	if(k>=cmin[now]) a+=cmin[now];//最后的A还可能在走一步
	return (double)a/b;//返回AB的比值
}

int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		scanf("%lld",&p[i].h);//每个点记录一个高度
		p[i].x=i;//编号是i
	}
	s[0].h=2e16;
	prepare();//预处理函数
	begina();//倍增预处理
	scanf("%d",&k);
	double mmin=2e16;
	int minx=0;
	for(int i=1;i<=n;i++){//枚举每个可能是解的城市
		double pp=solve(i,k);//求解比值
		if(mmin>pp){//更新mmin
			minx=i;
			mmin=pp;
		}
	}
	printf("%d\n",minx);//输出
	int m;
	scanf("%d",&m);
	for(int i=1;i<=m;i++){
		long long x,y;//输入x,y
		scanf("%lld %lld",&x,&y);
		double p=solve(x,y);//求解
		printf("%lld %lld\n",a,b);//输出a,b
	}
}

当然我也没说不让你用multi_set做


  

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值