线段树,堆,multiset:Holedox Eating

探讨Holedox在一个管道中寻找蛋糕并最小化移动距离的问题。通过三种方法——线段树、堆和multiset实现算法,每种方法各有特点。

Holedox Eating

Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 1187    Accepted Submission(s): 391


Problem Description
Holedox is a small animal which can be considered as one point. It lives in a straight pipe whose length is L. Holedox can only move along the pipe. Cakes may appear anywhere in the pipe, from time to time. When Holedox wants to eat cakes, it always goes to the nearest one and eats it. If there are many pieces of cake in different directions Holedox can choose, Holedox will choose one in the direction which is the direction of its last movement. If there are no cakes present, Holedox just stays where it is.
 

Input
The input consists of several test cases. The first line of the input contains a single integer T (1 <= T <= 10), the number of test cases, followed by the input data for each test case.The first line of each case contains two integers L,n(1<=L,n<=100000), representing the length of the pipe, and the number of events.
The next n lines, each line describes an event. 0 x(0<=x<=L, x is a integer) represents a piece of cake appears in the x position; 1 represent Holedox wants to eat a cake.
In each case, Holedox always starts off at the position 0.
 

Output
Output the total distance Holedox will move. Holedox don’t need to return to the position 0.
 

Sample Input
3 10 8 0 1 0 5 1 0 2 0 0 1 1 1 10 7 0 1 0 5 1 0 2 0 0 1 1 10 8 0 1 0 1 0 5 1 0 2 0 0 1 1
 

Sample Output
Case 1: 9 Case 2: 4 Case 3: 2


这道题神奇得可以用三种方法,线段树容易想到但难敲,堆相对容易,multiset这种奇淫巧计竟然也可以过:


线段树:

#include<iostream>
#include<stdio.h>
#include<string.h>
#include<queue>
#include<cmath>
#include<stack>
#include<algorithm>

using namespace std;

const int N=100005;
struct node
{
    int l,r;
    int sum;
}mem[N*3];

int num[N];//记录某个位置的食物数量
int place,suml,sumr;//小动物的位置 和它左边 右边的食物数量
int to;//表示方向 1 向右 0 向左
int ans;//保存答案
int L,R;//搜索左边和右边最靠近小动物的食物位置
int Search(int,int );
void insert(int ,int ,int );
void Right()//选择右边的食物 进行的一些必要更新
{
    to=1;
    sumr-=num[R];
    insert(1,R,-num[R]);
    --num[R];
    ans=ans+R-place;
    place=R;
}
void Left()//选择左边的食物 进行的一些必要更新
{
    to=0;
    suml-=num[L];
    insert(1,L,-num[L]);
    --num[L];
    ans=ans+place-L;
    place=L;
}
void build(int x,int i,int j)//建树
{
    mem[x].l=i;
    mem[x].r=j;
    mem[x].sum=0;
    if(i==j)
    return ;
    int mid=(i+j)>>1;
    build(x*2,i,mid);
    build(x*2+1,mid+1,j);
}
void insert(int x,int p,int k)//在p这个位置 插入k个食物 k可以为负 用来减少操作
{
    int mid=(mem[x].l+mem[x].r)>>1;
    if(mem[x].l==mem[x].r)
    {
        mem[x].sum+=k;
        return ;
    }
    if(p<=mid)
    insert(x*2,p,k);
    else
    insert(x*2+1,p,k);
    mem[x].sum=mem[x*2].sum+mem[x*2+1].sum;
}
int Search(int x,int d)//搜索第d个食物的位置
{
    if(mem[x].l==mem[x].r)
    return mem[x].r;
    if(mem[x*2].sum>=d)
    return Search(x*2,d);
    else
    return Search(x*2+1,d-mem[x*2].sum);
}
int main()
{
   int T;
   scanf("%d",&T);
   for(int w=1;w<=T;++w)
   {
       int n,m;
       place=0,suml=0,sumr=0;
       to=1;
       ans=0;
       scanf("%d %d",&n,&m);
       build(1,0,n);
       memset(num,0,sizeof(num));
       while(m--)
       {
           int k,x;
           scanf("%d",&k);
           if(k==0)
           {
               scanf("%d",&x);
               ++num[x];
               if(x!=place)//插入位置 不是在小动物位置才更新线段树
               insert(1,x,1);
               if(x<place)
               ++suml;
               else if(x>place)
               ++sumr;
           }else
           {
               if(num[place]>0)//在小动物位置 直接减少 不需其他操作
               {
                   --num[place];
                   continue;
               }
               if(suml==0&&sumr==0)//没有食物
               continue;
               if(sumr==0)//右边没食物 选左边的
               {
                   L=Search(1,suml);
                   Left();
               }else
               if(suml==0)//选右边的
               {
                   R=Search(1,1);
                   Right();
               }else
               if(suml>0&&sumr>0)
               {
                   L=Search(1,suml);//求的左边最近食物位置
                   R=Search(1,suml+1);//求的右边最近食物位置
                   if(place-L<R-place||(place-L==R-place&&to==0))
                   Left();
                   else
                   Right();
               }
           }
       }
       printf("Case %d: %d\n",w,ans);
   }
   return 0;
}

堆:

#include<iostream>
#include<queue>
#include<algorithm>
using namespace std;
priority_queue<int,vector<int>,greater<int> > minQue;
priority_queue<int> maxQue;
int main(){
	long long res;
	int k,l,n,left,targ,val,right,curr,dir;
	while(scanf("%d",&k)!=EOF){
		for(int i=1;i<=k;i++){
			while(!minQue.empty()) minQue.pop();
			while(!maxQue.empty()) maxQue.pop();
			res=0;
			curr=0;
			dir=1;
			scanf("%d%d",&l,&n);
			while(n--){
				scanf("%d",&targ);
				if(targ){
					if(minQue.empty()){
						if(!maxQue.empty()){
							left=maxQue.top();
							maxQue.pop();
							res+=curr-left;
							if(curr!=left) dir=0;
							curr=left;
						}
					}
					else{
						if(maxQue.empty()){
							right=minQue.top();
							minQue.pop();
							res+=right-curr;
							curr=right;
							dir=1;
						}
						else{
							left=maxQue.top();
							right=minQue.top();
							if(curr-left<right-curr){
								res+=curr-left;
								if(curr!=left) dir=0;
								curr=left;
								maxQue.pop();
							}
							else if(curr-left>right-curr){
								res+=right-curr;
								dir=1;
								curr=right;
								minQue.pop();
							}
							else{
								if(dir){
									res+=right-curr;
									dir=1;
									curr=right;
									minQue.pop();
								}
								else{
									res+=curr-left;
									if(curr!=left) dir=0;
									curr=left;
									maxQue.pop();
								}
							}
						}
					}
				}
				else{
					scanf("%d",&val);
					if(val<=curr) maxQue.push(val);
					else minQue.push(val);
				}
			}
			printf("Case %d: ",i);
			cout<<res<<endl;
		}
	}
	return 0;
}

multiset:

#include<iostream>
#include<cstring>
#include<cstdio>
#include<set>
#include<cmath>
using namespace std;
int main(){
	int t;
	long long sum;
	int L,n;
	int a,b;
	long long Min,p;
	scanf("%d",&t);
	for(int Case=1;Case<=t;Case++){
		scanf("%d%d",&L,&n);
		sum=0;
		multiset<int>s;                       //multiset用红黑树来组织元素数据,题目中需要允许重复插入元素键值,故不用set
		multiset<int>::iterator It,del;
		int cur=0,pre=0;
		while(n--){
			 scanf("%d",&a);
			 if(a==0){
				 scanf("%d",&b);
				 s.insert(b);
			 }
			 else{
				  Min=0x3fffffff;
				  if(!s.size())
					  continue;
				  for(It=s.begin();It!=s.end();It++){
					  if(abs(*It-cur)<Min){   
						  Min=abs((*It)-cur);
						  p=(*It);           //得到距离最近的那个蛋糕的位置
						  del=It;            //标记要吃的蛋糕,以便于删除
					  }
					  else if(abs(*It-cur)==Min){   //当距离一样时取同方向的
						  if((*It-cur)*(cur-pre)>0){
							  Min=abs((*It)-cur);
							  p=(*It);
							  del=It;
						  }
					  }
				  }
				  s.erase(del);
				  sum+=Min;
				  pre=cur;                    //之前所在的位置
				  cur=p;                      //目前的位置
			 }
		}
		printf("Case %d: %I64d\n",Case,sum);
	}
//	system("pause");
	return 0;
}





已经博主授权,源码转载自 https://pan.quark.cn/s/fb533687a163 《C++经典代码大全》是一部专门针对C++入门者的重要参考资料,其核心目标在于提供易于理解的C++编程范例,旨在协助新学者迅速领会C++语言的关键概念与技术要点。此压缩文件所包含的信息或许涵盖了从基础到高级的各类C++编程技巧,涉及面向对象编程中的类与对象、函数的应用、程序流程控制、数据结构设计、模板技术以及异常管理等多个关键领域。 1. **基础语法** - 变量声明与初始化:掌握如何声明并初始化不同数据类型的变量,例如整型(int)、浮点型(float)、字符型(char)等。 - 基本输入输出:学习运用`std::cin`和`std::cout`执行标准数据输入与输出操作。 - 控制流语句:熟练运用条件语句(if、if-else、switch-case)以及循环语句(for、while、do-while)来控制程序流程。 2. **类与对象** - 类的定义:学会如何构建类,包含其成员变量与成员函数的设定。 - 对象的创建与使用:掌握如何实例化对象,并经由对象访问类的成员函数。 - 封装:理解封装的理念,并学习使用private和public访问修饰符来保护数据。 - 构造函数与析构函数:掌握如何为类定义自定义的构造过程与析构过程。 3. **函数** - 函数的定义与调用:理解函数的功能与作用,以及如何进行函数的定义和调用。 - 函数参数:精通不同类型的参数传递方法,包括值传递和引用传递。 - 函数重载:学习在同一作用域内定义多个具有相同名称但参数列表不同的函数。 - 函数指针:了解函数指针的运用方法,及其在回调函数和模板中的应用场景。 4. **数组与字符串** -...
内容概要:本文研究了一种计及自适应预测修正的微电网模型预测控制(MPC)优化调度方法,并提供了Matlab代码实现。该方法针对微电网中风电出力等可再生能源的强不确定性,引入自适应预测修正机制,动态调整预测模型以提升短期功率预测精度,从而增强调度决策的准确性与系统运行的鲁棒性。研究构建了完整的MPC滚动优化框架,涵盖预测模型建立、多时间尺度优化求解、实时反馈校正等关键环节,实现了系统运行成本最小化、能源高效利用与功率平衡的多重目标。所提方法有效应对了负荷波动与新能源出力随机性带来的调度挑战,提升了微电网能量管理系统的智能化水平。; 适合人群:具备电力系统、自动化、控制理论或相关领域基础知识的研究生、科研人员及工程技术人员,尤其适合从事微电网优化、可再生能源集成、模型预测控制研究的专业人士,熟悉Matlab编程与优化算法者更佳。; 使用场景及目标:①应用于高比例可再生能源接入的微电网能量管理系统,提升调度方案的实时性与鲁棒性;②为不确定性环境下电力系统动态优化控制策略的研究提供仿真验证平台;③支持学术论文复现、科研课题攻关及实际工程项目的前期技术验证与方案预研。; 阅读建议:建议结合Matlab代码逐模块分析算法实现细节,重点关注预测模型构建与反馈修正机制的设计逻辑,通过调整风电出力、负荷需求等场景参数进行仿真实验,深入理解MPC在微电网调度中的滚动优化特性与自适应修正能力。
代码下载链接: https://pan.quark.cn/s/a4b39357ea24 在信息技术领域中,字符编码扮演着处理文本数据的核心角色。本文着重研究在微控制器系统中,运用C语言如何将UTF-8编码格式转换为GBK编码格式,旨在处理串口通信、TF卡存储或LCD显示屏上可能出现的中文显示错误问题。我们将详细剖析UTF-8与GBK编码的运作机制,并研究基于Keil开发平台的C语言实现流程。 UTF-8是一种被广泛接纳的Unicode字符编码方案,它采用可变长度的字节序列来表示字符,每个Unicode字符都对应一个独一无二的数字标识,即码点。UTF-8的一个显著特点是对ASCII字符(英文文本)保持不变,因此在网络传输和文件存储方面展现出优秀的兼容性。 GBK编码,正式名称为“汉字内码扩展规范”,是中国大陆的标准化编码,是对GB2312编码的延伸,总共涵盖了20902个汉字及其他符号,每个字符使用两个字节来表示。GBK在GB2312的基础上扩充了许多繁体字、少数民族文字以及特殊符号,目的是满足更广泛的语言需求。 将UTF-8转换为GBK的主要难点在于GBK是一种固定长度的双字节编码,而UTF-8则是可变长度的编码。转换过程中需要将UTF-8的多字节序列解析为相应的Unicode码点,然后依据GBK的编码规则查找匹配的编码。这一过程通常借助查表法完成,即建立一个从Unicode码点到GBK编码的映射库。 在Keil开发环境中,使用C语言实现UTF-8到GBK的转换可以遵循以下步骤: 1. **构建查表法所需的GBK编码库**:需要准备一个包含所有GBK字符二进制形式的GBK编码库。这个库通常是一个二进制文件,其大小大约为41KB。 2. **解析UTF-8编码**...
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值