002. 队列安排(洛谷P1160)

news/2024/12/27 1:49:02/文章来源:https://www.cnblogs.com/zyihan-crz/p/18631265

002. 队列安排(洛谷P1160)

题目描述

一个学校里老师要将班上 \(N\) 个同学排成一列,同学被编号为 \(1\sim N\),他采取如下的方法:

  1. 先将 \(1\) 号同学安排进队列,这时队列中只有他一个人;

  2. \(2\sim N\) 号同学依次入列,编号为 \(i\) 的同学入列方式为:老师指定编号为 \(i\) 的同学站在编号为 \(1\sim(i-1)\) 中某位同学(即之前已经入列的同学)的左边或右边;

  3. 从队列中去掉 \(M\) 个同学,其他同学位置顺序不变。

在所有同学按照上述方法队列排列完毕后,老师想知道从左到右所有同学的编号。

输入格式

第一行一个整数 \(N\),表示了有 \(N\) 个同学。

\(2\sim N\) 行,第 \(i\) 行包含两个整数 \(k,p\),其中 \(k\) 为小于 \(i\) 的正整数,\(p\)\(0\) 或者 \(1\)。若 \(p\)\(0\),则表示将 \(i\) 号同学插入到 \(k\) 号同学的左边,\(p\)\(1\) 则表示插入到右边。

\(N+1\) 行为一个整数 \(M\),表示去掉的同学数目。

接下来 \(M\) 行,每行一个正整数 \(x\),表示将 \(x\) 号同学从队列中移去,如果 \(x\) 号同学已经不在队列中则忽略这一条指令。

输出格式

一行,包含最多 \(N\) 个空格隔开的整数,表示了队列从左到右所有同学的编号。

样例 #1

样例输入 #1

4
1 0
2 1
1 0
2
3
3

样例输出 #1

2 4 1

提示

【样例解释】

将同学 \(2\) 插入至同学 \(1\) 左边,此时队列为:

2 1

将同学 \(3\) 插入至同学 \(2\) 右边,此时队列为:

2 3 1

将同学 \(4\) 插入至同学 \(1\) 左边,此时队列为:

2 3 4 1

将同学 \(3\) 从队列中移出,此时队列为:

2 4 1

同学 \(3\) 已经不在队列中,忽略最后一条指令

最终队列:

2 4 1

【数据范围】

对于 \(20\%\) 的数据,\(1\leq N\leq 10\)

对于 \(40\%\) 的数据,\(1\leq N\leq 1000\)

对于 \(100\%\) 的数据,\(1<M\leq N\leq 10^5\)

题解

解法一:

这道题是一道典型的模拟题,本人第一个想到的就是用数组(太年轻),然后每加一个同学,就把他后面的同学位置整体向后挪,将他放进去,然后

就没有然后了···

对于N,M ≤ 100000和这个无比粗暴的方法,不TLE就是奇迹


妄想偷懒不行,只能好好分析一下题

稍加观察可以看出,这是一个链式结构,移动同学肯定是不行的(时间复杂度太高),那么,只能对他们间的关系下手了

我们可以把每相邻的的两个同学想象成他们牵着手(如图) img 既然如此,我们就只需要改动左右手指向的同学就可以了 定义一个结构体

struct T{int l,r;        //每个同学的“左右手” 
}t[mx]={0};

现在我们就手动模拟一下加入同学(以右边为例) 将编号为J的同学加入编号为i的同学右边 示例

第一步 J的右手牵I右手牵的同学

第一步

t[j].r=t[i].r;

第二步 J的左手牵I

第二步

t[j].l=i;

第三步 I的右手牵J

第三步

t[i].r=j;

第四步 J右手牵的同学的左手牵J

注意:此时I的右手已经不牵原来那个同学了 第四步

t[t[j].r].l=j;

此时J就加入链当中了

左边同理

加入函数如下

void add(int i,int k,int f)       //新增同学 
{if(f==1)         //右 {t[k].r=t[i].r;t[k].l=i; t[i].r=k;t[t[k].r].l=k;}else             //左{t[k].r=i;t[k].l=t[i].l;t[i].l=k;t[t[k].l].r=k;}
}

接下来是移除

我们虽然也可以用上面方法来删除

不过我用的是另一种方法(主要是懒

我们可以给每个同学一个标记,标记了的将不会输出

struct T{int l,r;        //每个同学的“左右手” int d;          //表示同学是否输出 
}t[mx]={0};

结构体

while(m--){cin>>x;           //要删去的同学t[x].d=1;         //将该同学标记为不输出 }

标记


接下来是细节问题

链的初始化

如果我们将1同学先输入进去

t[1].l=1,t[1].r=1;

因为没有其他同学只能自己牵自己(紧紧抱住弱小的自己

但如果这样,到输出时就有一个问题(以样例为例) 样例 找不到开头!

这时我们就要在初始化时动些手脚

t[0].r=0,t[0].l=0;add(0,1,1);

定义个0同学 链将变成这样 链 我们只要从0的右手牵的同学开始输出,再到0结束就行了

输出代码

for (int i=t[0].r;i;i=t[i].r){if (t[i].d==0)    //输出未标记的 cout<<i<<" ";}

AC代码

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
using namespace std;
const int mx=1e5+10;
int n,m;
struct T{int l,r;        //每个同学的“左右手” int d;          //表示同学是否输出 
}t[mx]={0};
void add(int i,int k,int f)       //新增同学 
{if(f==1)         //左 {t[k].r=t[i].r;t[k].l=i; t[i].r=k;t[t[k].r].l=k;}else             //右 {t[k].r=i;t[k].l=t[i].l;t[i].l=k;t[t[k].l].r=k;}
}
int main()
{int x,k,f;cin>>n;t[0].r=0,t[0].l=0;add(0,1,1);for (int i=2;i<=n;i++){cin>>x>>f;add(x,i,f);}cin>>m;while(m--){cin>>x;t[x].d=1;         //将该同学标记为不输出 }for (int i=t[0].r;i;i=t[i].r){if (t[i].d==0)    //输出未标记的 cout<<i<<" ";}return 0;
}

解法二:

看了一下题解区似乎没有用STL的,那我就发个STL的解法好了(〃'▽'〃)

C++标准库自带了std::list这个双向链表的模板类,使用时需要包含头文件:#include 。

list<int> myList;

这句代码声明了一个list类型的变量,也就是一个包含int类型元素的双向链表。尖括号里边的部分称为模板参数,对于list而言,它表示链表里的元素是什么类型。

myList.push_front(1);
myList.push_back(2);

顾名思义,这两个成员函数分别用于在链表的头部和尾部插入元素。对应地,pop_front()用于移除头部的元素,pop_back()用于移除尾部的元素。

typedef list<int>::iterator Iter;
Iter itBegin = myList.begin();
Iter itEnd = myList.end();
for (; itBegin != itEnd; ++itBegin)printf(" %d", *itBegin);

这段代码演示的是list提供的,用于访问内部元素的迭代器。迭代器的类型是list::iterator(这里模板参数Tp需要与你操作的链表一致)。

迭代器的用法和指针有些像,可以用*运算符访问内部的元素,++和--运算符可以将它后移或前移一位(建议写成前置形式),用==和!=运算符进判断两个迭代器所指的位置是否一致。但要注意:list的迭代器不支持it += x或it1 - it2这样的运算,也不支持<,<=等运算符。

begin()成员函数返回指向头部元素的迭代器。

end()成员函数返回指向末尾位置的迭代器。这个“末尾位置”指的是最后一个元素再往后一位,也就是说end()所指的位置不包含有效元素,它相当于一个虚设的节点。这样设计是为了满足C++标准库表示区间时左闭右开的惯例。

//接上例
Iter it = myList.end();
--it;
//C++11中可以直接写成it = prev(myList.end());
//这里prev是头文件<iterator>提供的函数,用于返回将某个迭代器前移一位的结果
Iter it2 = myList.insert(it, 3);
//myList的内容:1,3,2

这段代码首先定义了一个迭代器it,然后在end()的基础上左移一位,让它指向链表中最后一个元素。

insert(it, val)成员函数用于在链表中插入元素。it为该链表的一个迭代器,val为待插入的值,插入后val位于it所指位置的前一位。返回值为一个迭代器,表示val插入到了哪个位置。

//接上例
myList.remove(it);
//myList的内容:1,3
int x = *it + 10; //ERROR!

remove(it)成员函数用于删除某个迭代器所指的节点。注意在删除之后it就失效了,除非给it重新赋值,否则对它的任何操作都会导致错误!

除上述主要操作以外,list还提供了其他一些实用的成员函数:size()返回链表内元素的个数,empty()判断链表是否为空,remove(val)用于移除所有值为val的节点,以及作为成员函数的sort()和unique()。(注意sort(myList.begin(), myList.end())是错误的写法)

有了这些基础知识,我们就可以用STL来写本题了。代码如下:

#include <cstdio>
#include <list>using namespace std;using Iter = list<int>::iterator;const int maxN = 1e5 + 10;
Iter pos[maxN];
list<int> queList;
bool erased[maxN];
int N;void buildQueue()
{queList.push_front(1);pos[1] = queList.begin();for (int i = 2; i <= N; i++){int k, p;scanf("%d%d", &k, &p);if (p == 0){pos[i] = queList.insert(pos[k], i); //left}else{auto nextIter = next(pos[k]);pos[i] = queList.insert(nextIter, i); //right}}int M;scanf("%d", &M);for (int x, i = 1; i <= M; i++){scanf("%d", &x);if (!erased[x]){queList.erase(pos[x]);}erased[x] = true;}
}int main()
{scanf("%d", &N);buildQueue();bool first = true;for (int x: queList){if (!first)putchar(' ');first = false;printf("%d", x);}putchar('\n');return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/858769.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

一个GLSL Shader的格式化算法(LALR解析器)

一个GLSL Shader的格式化算法(LALR解析器) 在进行OpenGL程序开发时,我需要自行解析`string`类型的Shader代码,抽取出里面的某些变量名和subroutine名。 由于找不到可用的GLSL Shader解析器,就照着虎书(《现代编译原理-c语言描述》)自己写了个LALR Generator,实际上包含了…

adb使用教程

谷歌官方出品用来控制安卓手机的工具1、作用打印日志定位bug稳定性测试运行设备的shell命令上传和下载文件安装和卸载设备上的应用等2、adb的安装配置Android开发官网下载ADB压缩包解压压缩包将ADB包放到根目录下将ADB路径加入到环境变量里3、用adb连接手机 进入开发者模式USB连…

中考阅读理解深入逻辑分析-005 A Tale of Bears and Belonging 熊的故事与归属感

文章正文 Dear Mr. Henshaw, ​ I finished Beggar Bears in two nights. It is a really good book. At first, I was surprised because it wasn’t funny like your other books, but then I got to thinking (you said readers should think) and decided a book …

PCIe扫盲——BDF与配置空间

前面的文章中介绍过,每一个PCIe设备可以只有一个功能(Function),即Fun0。也可以拥有最多8个功能,即多功能设备(Multi-Fun)。不管这个PCIe设备拥有多少个功能,其每一个功能都有一个唯一独立的配置空间(Configuration Space)与之对应。 和PCI总线一样,PCIe总线中的每一…

路由器透明代理

​1、下载OpenWrt 找各自路由型号----下载地址:https://firmware-selector.openwrt.org/​ 下载完后我们通过以下步骤让路由器进入刷机模式并准备好刷机:关闭路由器电源 按住复位键并接入电源,此时你会看到电源LED灯变成橙色等闪烁,接着会变为白色灯闪烁,此时可以放开复位…

SpringBoot 集成RabbitMQ

springboot集成MQ 配置文件配置类 发送者 消费者 调用

前端文档生成框架

背景 有时候写项目难免要用到文档框架。VitePress/VuePress 熟悉的vue,不必多说。 https://vitepress.dev/zh/ docsify 28.2k stars https://docsify.js.org/#/ 知名度很高。 docusaurus facebook出品。 https://docusaurus.io/zh-CN/ StoryBook 85k stars https://storybook.…

【YashanDB知识库】通过触发器复制varchar(4000 char)列的数据导致乱码

本文内容来自YashanDB官网,原文内容请见 https://www.yashandb.com/newsinfo/7802969.html?templateId=1718516 问题现象 某客户在使用触发器将varchar(4000 char)列的数据从表A复制到表B时,表B上列的数据与A上对应列的数据不同且为乱码。 如下截图为A的数据及使用触发器复制…

CMFCToolTipCtrl的AddTool导致内存增加

多次调用CMFCToolTipCtrl的AddTool会导致程序内存不断增加,尤其在循环中,因此需要AddTool之前,先进行判断,如果新文本与旧的不相同,才添加,并且添加之前先删除。需要注意的是,提示文本支持最大长度是MAX_TIP_TEXT_LENGTH.该宏在tooltip.cpp line:22定义,值为1024,文本…

09. 数字选择控件

一、数字选择控件数字选择控件提供了一对上下箭头,用户可以单击箭头选择数字,也可以直接输入。PySide6 中提供的数据选择控件主要有 QSpinBox(整数数字选择控件)和 QDoubleSpinBox(小数数字选择控件)。我们可以在终端中使用 pip 安装 pyside6 模块。 pip install pyside6…

Fiddler v5.0.2专业网络抓包工具简体中文版

点击上方蓝字关注我 前言 Fiddler抓包工具一个非常专业且已经翻译成中文的网络调试帮手。它就像一个超级监视器,能帮你看到电脑和互联网之间所有通过HTTP协议传输的信息。比如,当你在网上冲浪时,它可以记录下你和网站之间交换的所有“小纸条”,比如网页上的小饼干(cookie)…