洛谷 P1160 队列安排 题解

洛谷 P1160 队列安排 题解

题目大意

原题链接:洛谷 P1160 队列安排

\(N\) 个同学用以下方法排成一列:

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

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

  3. 从队列中去掉 \(M\) 个同学,其他同学位置顺序不变。若该同学已经不在队列中则忽略这一条指令。

排列完毕后,从左到右输出所有同学的编号。

题意分析

考察双向链表的使用。用数组模拟指针。定义一个结构体数组和一个首指针模拟链表:

struct list {
	int llink, rlink; /* llink 指向它的前驱(排在他左边的同学),rlink 指向它的后继(排在他右边的同学) */
};
list stu[100005]; // stu[i] 表示 i 号同学的前驱和后继
int head = 1; // head 指向链首

题目要求实现如下操作:

  1. 插入:如图,1、2 号同学正在排队,将 3 号同学插入到 1、2 号同学之间,需要将 1 号同学的后继改为 3 号同学,2 号同学的前驱改为 3 号同学,3 号同学的前驱改为 1 号同学、后继改为 2 号同学。

插入

void ins(int i, int k, int p) {
	if (p == 0) {
		if (head == k) head = i; // 注意处理 k 为队首的情况
		stu[i].llink = stu[k].llink;
		stu[stu[i].llink].rlink = i;
		stu[i].rlink = k;
		stu[k].llink = i;
	} else {
		stu[i].rlink = stu[k].rlink;
		stu[stu[i].rlink].llink = i;
		stu[i].llink = k;
		stu[k].rlink = i;
	}
}
  1. 删除:如图,1、2、3 号同学正在排队,删除 2 号同学,只需要将 1 号同学的后继改为 3 号同学,将 3 号同学的前驱改为 1 号同学。

删除


int Del[100005]; // 记录 i 号同学是否已被删除
void del(int x) {
	if (Del[x]) return; // 如果已被删除,则忽略这条指令
	Del[x] = 1;
	if (head == x) head = stu[x].rlink; // 特判删掉队首的情况
	stu[stu[x].llink].rlink = stu[x].rlink;
	stu[stu[x].rlink].llink = stu[x].llink;
}

AC 代码

#include <iostream>
using namespace std;

struct list {
	int llink, rlink;
};

list stu[100005];
int n, m, head = 1;
int Del[100005];

void ins(int i, int k, int p) {
	if (p == 0) {
		if (head == k) head = i;
		stu[i].llink = stu[k].llink;
		stu[stu[i].llink].rlink = i;
		stu[i].rlink = k;
		stu[k].llink = i;
	} else {
		stu[i].rlink = stu[k].rlink;
		stu[stu[i].rlink].llink = i;
		stu[i].llink = k;
		stu[k].rlink = i;
	}
}

void del(int x) {
	if (Del[x]) return;
	Del[x] = 1;
	if (head == x) head = stu[x].rlink;
	stu[stu[x].llink].rlink = stu[x].rlink;
	stu[stu[x].rlink].llink = stu[x].llink;
}

int main() {
	cin >> n;
	for (int i = 2; i <= n; i++) {
		int k, p;
		cin >> k >> p;
		ins(i, k, p);
	}
	cin >> m;
	while (m--) {
		int x;
		cin >> x;
		del(x);
	}
	for (int i = head; i != 0; i = stu[i].rlink) {
		cout << i << " ";
	}
	return 0;
}

代码的简略写法

可以将 0 号同学作为虚拟队首,这样就无需特判队首的情况了。

#include <iostream>
using namespace std;

struct list {
	int llink, rlink;
};

list stu[100005];
int n, m, head = 1;
int Del[100005];

void ins(int i, int k, int p) {
	if (p == 0) {
		stu[i].llink = stu[k].llink;
		stu[stu[i].llink].rlink = i;
		stu[i].rlink = k;
		stu[k].llink = i;
	} else {
		stu[i].rlink = stu[k].rlink;
		stu[stu[i].rlink].llink = i;
		stu[i].llink = k;
		stu[k].rlink = i;
	}
}

void del(int x) {
	if (Del[x]) return;
	Del[x] = 1;
	stu[stu[x].llink].rlink = stu[x].rlink;
	stu[stu[x].rlink].llink = stu[x].llink;
}

int main() {
	cin >> n;
	stu[0].rlink = 1; // 用 stu[0].rlink 作为队首
	for (int i = 2; i <= n; i++) {
		int k, p;
		cin >> k >> p;
		ins(i, k, p);
	}
	cin >> m;
	while (m--) {
		int x;
		cin >> x;
		del(x);
	}
	for (int i = stu[0].rlink; i != 0; i = stu[i].rlink) {
		cout << i << " ";
	}
	return 0;
}

另一种做法

C++ STL 同样封装好了链表以简化操作(需要 #include<list> ),下面是一些常见操作:

  1. list<int> a :定义一个 int 类型的链表 a。

  2. list<int>::iterator it; :定义一个名为 it 的迭代器。

  3. a.begin(); a.end() :链表开始和末尾的迭代器(左闭右开)。

  4. it++; it--; :迭代器指向下一个和上一个元素。

  5. a.push_back(x) :在链表末尾插入元素 x。

  6. a.insert(it, x) :在迭代器 it 插入元素 x。

  7. a.erase(it) :删除迭代器 it 所在元素。

设置一个迭代器数组 pos[i] 指向 i 号同学在链表中的位置。

AC 代码

#include <iostream>
#include <list>
#include <iterator>
using namespace std;

list<int> a;
int n, m, del[100005];
list<int>::iterator pos[100005]; // pos[i] 为指向 i 号同学在链表 a 中位置的迭代器

int main() {
    cin >> n;
    a.push_back(1);
    pos[1] = a.begin();
    for (int i = 2; i <= n; i++) {
        int k, p;
        cin >> k >> p;
        if (p == 0) {
            pos[i] = s.insert(pos[k], i);
        } else {
            list<int>::iterator it = pos[k]; // 类型也可以写为 auto
            it++; // 注意:因为 insert 函数是在迭代器前插入元素,所以要 it++
            pos[i] = s.insert(it, i);
        }
    }

    cin >> m;
    while (m--) {
        int x;
        cin >> x;
        if (!del[x]) {
            s.erase(pos[x]);
            del[x] = 1;
        }
    }
    
    for (int x : a) { // 使用变量 x 遍历链表 a,需要 C++11 以上
        cout << x << " ";
    }
    return 0;
}
posted @ 2026-08-22 11:26  zzx321  阅读(2)  评论(0)    收藏  举报