洛谷P1160 队列安排(Problem B)超级废话版!!!

该文讲述了如何使用结构体数组和STL的list容器实现一种特殊的队列排列,其中涉及节点的插入和删除操作。在链表中,通过结构体存储每个节点的左右连接,并用额外的数组标记节点是否被删除。文章详细解析了插入和删除的逻辑,包括处理节点间关系和头节点的变化。
摘要由CSDN通过智能技术生成

p1160队列安排原题链接:队列安排 - 洛谷

        这一题很显然是考链表的知识点,这里我是用结构体数组和STL的list容器两种方法实现。

ps:可以直接看最下面的代码

结构体数组实现的过程:

下面是对题目的分析(两种方式是一样的思路):

        首先创建一个有"L"和"R"的结构体(下标i即代表第i个),用来表明每一个i左右边连接的数。

并且L和R都赋初始值为0,值为0就像指向空。

        

在一开始创建完结构体后大概长这样:      

         因为是用数组来表现链式结构,所以我们不知道在最终排完队后谁是第一个同学,所以可以用一个"head"来表示排在第一个的同学。

 这一句即是把"head"赋值为1,但不会改变上图。

 这一句我们用一个for循环就可以解决,for中的i从2开始。

 这里说明有删除操作,并且结合下面的输入说明和样例得知,同一个数可能被删除多次,所以为了防止删除同一个数字而导致的其他问题,这里可以用一个数组来表示这个同学是否有被删除过,在开始删除前,可以把数组全部赋值为1,表示没被删除。

了解完大概题意后能大概得到下面这个框架:

const int N = 100005;
struct node {
	int l = 0, r = 0;
}L[N];
int h[N];
int main() {
	int n, m, head = 1;//初始化头节点为第一个同学
	cin >> n;
	memset(h, 1, sizeof(h));//h数组用来检查第i个同学有没有被删除,置1。
	int k, p;
	for (int i = 2; i <= n; ++i) {
		scanf("%d %d", &k, &p);

	}
	return 0;
}

然后根据输入说明 

我们开始写for循环里的内容,

 这是适用普通情况的,但我们还需要考虑p==0时,k的左边还有同学,也就是i插在两个同学之间,这时我们就要考虑i和k左边的那个同学的关系了。以样例说明,在执行完i=3两个操作后,为下图的黑色部分,而在i=4(k=1,p=0)的时候,我们不仅要修改i和k的关系,也要修改L[L[k].l].r和i的关系,如下图:

所以,在p==0的时候需要先判断k的左边是否还有同学,当左边还有同学的时候,我们应该先修改k左边的同学和i的关系,

 注意这里的这个if(L[k].l)必须先写,如果是先写了i和k的关系修改,那样L[k].l就等于i了,就找不到原本k左边的同学了。当p==1时,步骤和这里是一样的。

此外,在p==0的时还有注意head的变化,当我们要在k的左边插入i,k原本又是排在最左边的,我们就需要将head=i了。   在执行完for语句后,结构体数组变成下面这样:

         然后是删除操作,我们前面创建了一个h数组用于判断是否被删除,于是我们可以用一个if(h[cur])就可以判断是否要删除它了,在这里可以先用一个tmp来记录L[cur].l,这样是为了修改要删除的数的右边的那个数的左边,也就是L[L[cur].r].l的值。这里我们也要注意头节点head的变化,当我们删除的当前节点cur就是头节点时,我们要把头节点传给cur右边的的节点:head=L[cur].r。

        最后,我们用一个循环来输出队伍,并且初始的i=head,每次让i=L[i].r,当i==0时退出即可。

  具体代码:

#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<list>
#include<cstring>
#include<string>
using namespace std;
const int N = 100005;
struct node {
	int l = 0, r = 0;
}L[N];
int h[N];
int main() {
	int n, m, head = 1;//初始化头节点为第一个同学
	cin >> n;
	memset(h, 1, sizeof(h));//h数组用来检查第i个同学有没有被删除,置1。
	int k, p;
	for (int i = 2; i <= n; ++i) {
		scanf("%d %d", &k, &p);
		if (p == 0) {
			if (L[k].l) {//如果k同学左边还有同学
				L[L[k].l].r = i;//先修改k左边的同学的右边为i
				L[i].l = L[k].l;
			}
			L[i].r = k;//注意不能写在if (L[k].l)前。
			L[k].l = i;
			if (head == k) {//k为头节点的话,i又插入在k左边,则将head=i;
				head = i;
			}
		}
		else {
			//同上
			if (L[k].r) {
				L[L[k].r].l = i;
				L[i].r = L[k].r;
			}
			L[i].l = k;
			L[k].r = i;
		}
	}
	cin >> m;
	int cur;
	while (m--) {
		scanf("%d", &cur);
		if (h[cur]) {//cur没有被删除
			int tmp = L[cur].l;
			if (L[cur].l) {
				L[L[cur].l].r = L[cur].r;
			}else {//如果左边没有节点,则需要修改头节点
				head = L[cur].r;
			}
			if (L[cur].r) {
				L[L[cur].r].l = tmp;
			}
			h[cur] = 0;
		}

	}
	for (int i = head; i != 0; i = L[i].r) {
		printf("%d ", i);
	}
	return 0;
}

用list实现:

#include <list>
#include<iostream>
using namespace std;
const int N = 100005;//注意N的范围是1e5 
list<int>::iterator pos[N];
list<int> queList;
int h[N];
int main()
{
    int N;
    scanf("%d", &N);
    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);
        }
        else
        {
            list<int>::iterator nextIter = next(pos[k]);
            pos[i] = queList.insert(nextIter, i);
        }
    }
    int M;
    cin >> M;
    for (int i = 1; i <= M; i++)
    {
        int x;
        cin >> x;
        if (!h[x]) {
            h[x] = 1;
            queList.erase(pos[x]);
        }
    }
    list<int>::iterator it = queList.begin();
    for (int i = 1; i <= queList.size(); i++)
    {
        cout << *it << " ";
        it++;
    }
    return 0;
}

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值