洛谷 P1160 队列安排

【题目链接】

洛谷 P1160 队列安排

【题目考点】

1. 链表(双向链表)
2. list

【解题思路】

解法1:手动实现双向链表

设数组na,na[i]表示第i名同学对应的结点的地址。na[i]为0表示第i名同学已被移去。
头插、尾插、插入函数都会返回插入结点的地址,将该地址保存在na数组中。
双向链表中的insert函数的作用为在地址it指向的元素前插入元素。若需在it指向的元素后插入,则应该先让it指向下一个元素,而后调用insert。
删除同学,直接调用删除结点的函数erase函数即可。

解法2:使用STL list

设数组na,类型为list<int>::iteratorna[i]表示指向第i名同学对应结点的迭代器。na[i]为lis.end()表示第i名同学已被移去。
其它做法与解法1类似。

【题解代码】

解法1:手动实现双向链表
#include<bits/stdc++.h>
using namespace std;
#define N 100005
template<class T>
struct Node
{
    T val;
    int next, pre;
};
template<class T>
struct List
{
    Node<T> node[N]; //内存池
    int p; //当前可分配的元素的地址
    int head; //头结点地址
    int tail; //尾结点地址
    List()
    {
        p = 0;
        head = ++p;
        tail = head;
    } 
    int push_front(T d)//头插法 返回插入结点的地址 
    {
        int np = ++p;
        node[np].val = d;
        node[np].next = node[head].next;
        node[np].pre = head;
        node[node[head].next].pre = np;//node[head].next为0,不影响 
        node[head].next = np;
        if(tail == head)
            tail = np;
        return np;
    }
    int push_back(T d)//尾插法 返回插入结点的地址 
    {
        int np = ++p;
        node[np].val = d;
        node[tail].next = np;
        node[np].pre = tail;
        tail = np;
        return np;
    }
    int getAddr(int i)//获取第i个结点的地址(i从1开始数)
    {
        int ip = head; //第i个节点的地址
        for(int j = 1; j <= i; j++)
            ip = node[ip].next;
        return ip;
    }
    int insert(int t, T d)//在t指向的结点前插入结点 返回插入的结点的地址 
    {       
        int np = ++p;
        node[np].val = d;
        node[np].next = t;
        node[np].pre = node[t].pre;
        node[node[t].pre].next = np;
		node[t].pre = np;
        return np;
    }
    int erase(int t)//删除t指向的结点 返回删除的结点的下一个结点的地址 
    {
    	node[node[t].pre].next = node[t].next;
    	node[node[t].next].pre = node[t].pre;
        if(tail == t)//如果删掉的是尾结点
            tail = node[t].pre; //把尾指针设为前一个结点 
        return node[t].next;
    }
    T get(int t)//获取t指向的结点的值 
    {
    	return node[t].val;
	}
    int begin()//第一个结点的地址 
    {
        return node[head].next;
    }
    int end()//最后一个结点的下一个结点的地址 
    {
        return 0;
    }
    int next(int t)//t指向结点下一个结点的地址 
    {
        return node[t].next;
    }
    int pre(int t)//t指向结点前一个结点的地址 
    {
        return node[t].pre;
    }
};
List<int> li;
int pos[N];//pos[i]:第i号同学的地址 
int main()
{
    int n, k, p, t, np, m, x;
    cin >> n;
    pos[1] = li.push_front(1);//1号同学进队列 
    for(int i = 2; i <= n; ++i)
    {
        cin >> k >> p;
        if(p == 0)//k左边插入 
            np = li.insert(pos[k], i);//在第k号同学结点地址pos[k]左边插入值i 
        else//k右边插入 
        {
            if(li.next(pos[k]) == li.end())//如果k的下一个结点是end(),说明k是最后一个结点,需要用尾插法将i插到k右边 
                np = li.push_back(i);
            else//k下一个结点的左边插入i
                np = li.insert(li.next(pos[k]), i);
        }
        pos[i] = np;//记录第i号同学的结点地址是np 
    }
    cin >> m;//m次删除 
    for(int i = 1; i <= m; ++i)
    {
        cin >> x;//删除x号同学 
        if(pos[x] > 0)//若pos[x]为0,则说明x号学生被移除了。避免重复删除。 
        {
            li.erase(pos[x]);
            pos[x] = 0;
        }
    }
    for(int i = li.begin(); i != li.end(); i = li.next(i))//遍历链表 输出 
        cout << li.get(i) << ' ';
    return 0;
}
解法2:
#include <bits/stdc++.h>
using namespace std;
typedef list<int>::iterator It;
#define N 100005
It na[N];//na[i]:指向表示第i同学结点的迭代器 na[i]为lis.end(),则表示第i同学已经不在队列之中 
list<int> lis;
int main()
{
	int n, k, p, t, m, x;
	It ip;
	cin >> n;
	lis.push_back(1);//添加第1名同学 
	na[1] = lis.begin();
	for(int i = 2; i <= n; ++i)
	{
		cin >> k >> p;
		ip = na[k];//指向第k号同学的迭代器 
		if(p == 1)//如果在第k号同学右面插入 
			ip++;//那么ip加1后,在ip指向的结点左边插入 
		na[i] = lis.insert(ip, i);//在ip指向的结点左边插入值为i的新结点,指向该结点的迭代器保存在na[i]
	}
	cin >> m;
	for(int i = 1; i <= m; ++i)
	{
		cin >> x;
		if(na[x] != lis.end())//如果第x号同学还在队列内 
		{
			lis.erase(na[x]);//删掉第x号同学 
			na[x] = lis.end();//na[x]设为lis.end()表示第x号同学已经不在队列内 
		}
	}
	for(It it = lis.begin(); it != lis.end(); it++)//遍历list,输出链表 
		cout << *it << ' ';
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值