首页 > 其他分享 >002. 队列安排(洛谷P1160)

002. 队列安排(洛谷P1160)

时间:2024-12-25 19:08:10浏览次数:3  
标签:同学 洛谷 迭代 int list 链表 002 P1160 myList

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;
}

标签:同学,洛谷,迭代,int,list,链表,002,P1160,myList
From: https://www.cnblogs.com/zyihan-crz/p/18631265

相关文章

  • 001. 约瑟夫问题(洛谷P1996)
    001.约瑟夫问题(洛谷P1996)题目描述\(n\)个人围成一圈,从第一个人开始报数,数到\(m\)的人出列,再由下一个人重新从\(1\)开始报数,数到\(m\)的人再出圈,依次类推,直到所有的人都出圈,请输出依次出圈人的编号。注意:本题和《深入浅出-基础篇》上例题的表述稍有不同。书上表述是......
  • 线段树1模板 (洛谷p3372)
    关键在于创建一个向上返回的函数up,向下查询的同时将父亲sum和add值传给儿子函数down最后用lazy函数更新sum和add的值先通过build函数是sum数组完成初始化,然后用adds已经quiey函数完成求解,详细注释见代码​#include<iostream>#include<cstdio>usingnamespacestd;intm......
  • 英锐芯AD8002B高性能的AB类音频功率放大器
    8002B是一款高性能的AB类音频功率放大器。它具有以下特点:1.单声道带关断模式:8002B可以通过控制进入休眠模式,从而减少功耗。它还具有过热自动关断保护机制,确保芯片的安全运行。2.高驱动功率:8002B在4Ω负载下,THD小于10%时可以提供最大驱动功率为3W,而在THD小于1%时可以提供2......
  • gesp(三级)(9)洛谷:B3956:[GESP202403 三级] 字母求和
    gesp(三级)(9)洛谷:B3956:[GESP202403三级]字母求和题目描述小杨同学发明了一种新型密码,对于每一个小写英文字母,该小写字母代表了一个正整数,即该字母在字母顺序中的位置,例如字母a代表了正整数1......
  • gesp(三级)(10)洛谷:B3957:[GESP202403 三级] 完全平方数
    gesp(三级)(10)洛谷:B3957:[GESP202403三级]完全平方数题目描述小杨同学有一个包含nnn个非负整数的序列A......
  • 【静态网页模板源码】000023 建筑工作室网站-响应式 (附源码)
    前言:哈喽,大家好,今天给大家分享一篇文章!并提供具体代码帮助大家深入理解,彻底掌握!创作不易,如果能帮助到大家或者给大家一些灵感和启发,欢迎收藏+关注哦......
  • 中考阅读理解深入逻辑分析-002 Battle of the Classroom: Bits vs. Books 课堂之战:比
    文章正文Thedebateon“textbooksvscomputers”hasbeengoingonforyears.Howmuchtechnologyisintheclassroom?Shouldtextbooksbeplacedbynotebookcomputers?Theisnodoubtthatcomputersarepowerful.Computer-basedlessonplansareupdatedinr......
  • SD模块-专题方案-销项税MWST税率确定逻辑(事务码VK11 & 后台表A002 & KNOH & KONP)
    销项税税率确定逻辑如下:1处,显示客户税分类来源于BP客户主数据-销售与分销视图2处,显示物料税分类来源于物料主数据销售视图23处,显示VK13维护的销项税税率17%4处,显示VA43销售合同中最终进行的销项税税率确定的17%税率事务码VK11维护销项税税率还是需要使用事务码FTXP查看......
  • d3dcompiler_42.dll未被指定在Windows运行,代码0xc0000020或0xc000012f解决办法
    在大部分情况下出现我们运行或安装软件,游戏出现提示丢失某些DLL文件或OCX文件的原因可能是原始安装包文件不完整造成,原因可能是某些系统防护软件将重要的DLL文件识别为可疑,阻止并放入了隔离单里,还有一些常见的DLL文件缺少是因为系统没有安装齐全的微软运行库,还有部分情况是因为......
  • d3dcompiler_41.dll未被指定在Windows运行,代码0xc0000020或0xc000012f解决办法
    在大部分情况下出现我们运行或安装软件,游戏出现提示丢失某些DLL文件或OCX文件的原因可能是原始安装包文件不完整造成,原因可能是某些系统防护软件将重要的DLL文件识别为可疑,阻止并放入了隔离单里,还有一些常见的DLL文件缺少是因为系统没有安装齐全的微软运行库,还有部分情况是因为......