题188.洛谷P1563 模拟-[NOIP2016 提高组] 玩具谜题

news/2024/7/2 1:24:28 标签: 算法, 队列, 模拟

文章目录

  • 题188.洛谷P1563 模拟-[NOIP2016 提高组] 玩具谜题
  • 一、题目
  • 二、题解


题188.洛谷P1563 模拟-[NOIP2016 提高组] 玩具谜题


一、题目

在这里插入图片描述
在这里插入图片描述

二、题解

本题要你得到从开始的小人开始执行指令到最后位于哪一个小人,过程中包含了要针对小人不同的朝向–向内向外以及变更小人时的指令方向–向左向右。因为变动过程中会出现"绕圈现象",所以不难想到可以用队列模拟过程。 我们把每个小人按照输入顺序作为编号存入队列中。
1.当小人朝内,指令向左时,每动一次就将队尾元素拿到队头,指令向右时,每动一次就将队头元素拿到队尾。做完指令后的队头元素即为变动到的小人编号。拿样例1的第一条指令为例,0编号小人朝内,指令向左(往左手方向),做"队尾甩队头",三次移动变动后到编号4:
在这里插入图片描述
2.当小人朝外时,过程正好和朝内时相反,指令向左时做"队头甩队尾",指令向右时做"队尾甩队头"。同样队头取结果
综上,队列应选用双端队列deque合适。虽然这个模拟过程十分直接,但由于m,n可能很大,导致了部分测试点超时,所以这个代码只能拿80分。代码如下:

#include <bits/stdc++.h>

using namespace std;

struct People
{
    string name;
    int direction;
}P[100001];//下标为那个小人的编号

int main()
{
    int n,m;
    cin>>n>>m;
    deque<int> dq;
    for(int i=1;i<=n;i++)
    {
        cin>>P[i].direction>>P[i].name;
        dq.push_back(i);//初始化队列,队尾插入元素
    }
    for(int i=0;i<m;i++)
    {
        int way,num;
        cin>>way>>num;
        int start=dq.front();//队头拿到初始元素
        while(num)
        {
            if(P[start].direction==0)//小人方向朝内
            {
                if(way==0)//指令向左
                {
                    int temp=dq.back();//取队尾元素
                    dq.pop_back();//删除队尾元素
                    dq.push_front(temp);//队头插入
                }
                else//指令向右
                {
                    int temp=dq.front();//取队头元素
                    dq.pop_front();//删除队头元素
                    dq.push_back(temp);//队尾插入
                }
            }
            else
            {
                if(way==1)
                {
                    int temp=dq.back();
                    dq.pop_back();
                    dq.push_front(temp);
                }
                else
                {
                    int temp=dq.front();
                    dq.pop_front();
                    dq.push_back(temp);
                }
            }
            num--;
        }
    }
    cout<<P[dq.front()].name<<endl;//始终是队头为结果,取结果输出
}

拓:关于deque的用法

下面介绍满分代码思路。因为队列模拟是变动时一个一个真的去变,循环用了两层,效率比较低,现想法子去掉一层循环。因为每次变动是往一个方向变,只是变动的步数不一样,所以我们不妨直接一变变到底,但是这样我们就必须舍去直接用队列模拟了。模仿队列模拟的思路,其实我们要做到"绕圈现象",就是增大编号时超最大编号范围了做一波用小人总数求余就好了,但是这样不是只能往一个方向变了么?毕竟减小编号时超了范围做求余的话就直接整出个负数编号出来了。所以为了能拓展减小编号的方向,所以我们再整一个逆着输入顺序的编号,这样我们可以直接做增大编号等价于正着编号时做减小编号。这样我们对发出两个方向的指令就都可以模拟到了。代码如下:

#include <bits/stdc++.h>

using namespace std;

struct People
{
    string name;
    int direction;
} P[100000],ReP[100000];//依旧是下标表示name对应小人的编号

int main()
{
    int n,m;
    cin>>n>>m;
    for(int i=0; i<n; i++)//必须从0开始放,不然你求余得位置要出事
    {
        cin>>P[i].direction>>P[i].name;
        ReP[n-i-1].direction=P[i].direction;
        ReP[n-i-1].name=P[i].name;
    }
    //start:当前所处的位置,这个位置可能时正向编号表里的位置,也可能是逆向编号表里的位置
    //flag:表示当前所处的状态,1表示使用正向编号,0表示使用逆向编号
    int start=0,flag=1;
    for(int i=0; i<m; i++)
    {
        int way,num;
        int direction;
        cin>>way>>num;
        if(flag==1)//记得每次都看下当前是处于正向编号表还是逆向编号表
        {
            direction=P[start].direction;
        }
        else
        {
            direction=ReP[start].direction;
        }
        if(direction==0)//小人方向朝内
        {
            if(way==0)//指令向左,做"队尾甩队头",使用逆向编号
            {
                if(flag==1)//如果此前是用的正向编号则要将start更改到逆向编号时那个name对应的小人的位置
                {
                    start=n-start-1;
                    flag=0;//同时变更flag
                }
                start=(start+num)%n;//做增大编号求余操作
            }
            else//指令向右,做"队头甩队尾",使用正向编号
            {
                if(flag==0)//同理
                {
                    start=n-start-1;
                    flag=1;
                }
                start=(start+num)%n;
            }
        }
        else//朝外,过程相反,同理
        {
            if(way==1)
            {
                if(flag==1)
                {
                    start=n-start-1;
                    flag=0;
                }
                start=(start+num)%n;
            }
            else
            {
                if(flag==0)
                {
                    start=n-start-1;
                    flag=1;
                }
                start=(start+num)%n;
            }
        }
    }
    if(flag==1)//看是用正向编号位置的小人输出还是逆向编号位置的小人输出
    {
        cout<<P[start].name<<endl;
    }
    else
    {
        cout<<ReP[start].name<<endl;
    }
}



http://www.niftyadmin.cn/n/1061872.html

相关文章

DB 分库分表的基本思想和切分策略

DB 分库分表的基本思想和切分策略一、基本思想Sharding的基本思想就要把一个数据库切分成多个部分放到不同的数据库(server)上&#xff0c;从而缓解单一数据库的性能问题。不太严格的讲&#xff0c;对于海量数据的数据库&#xff0c;如果是因为表多而数据多&#xff0c;这时候适…

nginx学习之——虚拟主机配置

例子1: 基于域名的虚拟主机 server { listen 80; #监听端口 server_name a.com; #监听域名 location / { root /var/www/a.com; #根目录定位 index index.html; } } 例子2: 基于端口的虚拟主机配置 server { listen 8080; server_name 192.168.1.204; location / { root /v…

关于jbpm的文档

一个引擎多个service&#xff1a; RepositoryService repositoryService processEngine.getRepositoryService(); //部署服务器 ExecutionService executionService processEngine.getExecutionService(); //执行服务器 TaskService taskService processEngin…

题190.2022寒假天梯赛训练-7-6 前世档案 (20 分)

文章目录题190.2022寒假天梯赛训练-7-6 前世档案 (20 分)一、题目二、题解题190.2022寒假天梯赛训练-7-6 前世档案 (20 分) 一、题目 二、题解 观察并分析题目所给的二叉树示例可知&#xff0c;我们设向左&#xff08;是&#xff09;为0&#xff0c;向右&#xff08;否&#xf…

postgresql 9.1 基于wal的完全恢复

基于wal恢复前的准备 确保这两个参数是启用状态 archive_mode on # allows archiving to be done archive_command cp %p /mnt/nas_dbbackup/archivelog/%f && echo %f >>/mnt/nas_dbbackup/archivelog/archive.log # command to use to archive a logfile s…

3573: [Hnoi2014]米特运输 - BZOJ

Description米特是D星球上一种非常神秘的物质&#xff0c;蕴含着巨大的能量。在以米特为主要能源的D星上&#xff0c;这种米特能源的运输和储存一直是一个大问题。 D星上有N个城市&#xff0c;我们将其顺序编号为1到N&#xff0c;1号城市为首都。这N个城市由N-1条单向高速通…

IT行业能力细分

在软件行业工作7年了&#xff0c;平时很懒&#xff0c;懒得做分享&#xff0c;今天特意分享一下软件行业&#xff0c;职业大的技术分类&#xff0c;同学们可根据自己职业规划补充学习知识块。 转载于:https://www.cnblogs.com/101Love/p/6721535.html

题191.2022寒假天梯赛训练-7-10 关于堆的判断 (25 分)

文章目录题191.2022寒假天梯赛训练-7-10 关于堆的判断 (25 分)一、题目二、题解题191.2022寒假天梯赛训练-7-10 关于堆的判断 (25 分) 一、题目 二、题解 本题要注意两个地方&#xff0c;第一个是判断输入是问的哪个问题&#xff0c;还有一个是建堆。前者感觉更不好处理些&…