洛谷Oj-队列安排-模拟双向链表

来源:互联网 发布:java 线程池 获取队列 编辑:程序博客网 时间:2024/05/22 05:26

问题描述
一个学校里老师要将班上N个同学排成一列,同学被编号为1~N,他采取如下的方法:
1.先将1号同学安排进队列,这时队列中只有他一个人;
2.2~N号同学依次入列,编号为i的同学入列方式为:老师指定编号为i的同学站在编号为1~i -1中某位同学(即之前已经入列的同学)的左边或右边;
3.从队列中去掉M(M

int mark[100001];int main(){    list<int>a;//双向链表a    int n,m,i,k,p,t;    list<int>::iterator it;//相当于指针的迭代器it    scanf("%d",&n);//输入n    a.push_back(1);//将编号为1的同学加入链表    for(i=2;i<=n;i++)//依次将编号为2到n的同学加入链表    {           scanf("%d%d",&k,&p);        it=find(a.begin(),a.end(),k);//在链表中查找编号为k的同学        if(p==1)//如果p为0,直接加入;p为1,则需要将it自增。注意it只能自增自减,不能加减一个常数            it++;        a.insert(it,i);//加入    }    scanf("%d",&m);//输入m    for(i=1;i<=m;i++)    {        scanf("%d",&t);//输入要去掉的同学的编号        if(mark[t])//如果编号为t的同学已经被去掉过            continue;        it=find(a.begin(),a.end(),t);//在链表中查找编号为t的同学        if(it!=a.end())//如果找到了。其实此语句对本题来说是多余的,因为一定能找到            a.erase(it);//删除迭代器位置上的元素        mark[t]=1;//将t标记    }    for(it=a.begin();it!=a.end();it++)//遍历链表        printf("%d ",*it);//打印    printf("\n");    return 0; }

代码②(用指针实现双向链表+记忆化 超时)

struct node{    int data;//同学的编号    node *prev,*next;//每个的名称前都要有‘*’};node *nil,*a;//nil为指向头结点的指针node *dp[100001];//记忆化数组int mark[100001];//标记数组void init()//链表初始化{    nil=(node*)malloc(sizeof(node));    nil->next=nil;    nil->prev=nil;}void insert(node* pros,int data)//将数据data插入链表中指针pors指向的节点的右侧{    node *x=(node *)malloc(sizeof(node));    x->data=data;    x->next=pros->next;//以下为插入的四个步骤,难以用少量文字说明,请在纸上模拟这个过程    pros->next->prev=x;    pros->next=x;    x->prev=pros;}node* search(int key)//在链表中查找值为key的节点{    if(dp[key]!=NULL)//如果被记录过,直接返回        return dp[key];    node *cur=nil->next;//从头节点开始查找    while(cur!=nil&&cur->data!=key)//如果链表遍历完毕或节点被成功找到,就跳出循环        cur=cur->next;    return dp[key]=cur;//返回并记忆化}void deletenode(node *t)//链表节点的删除{    if(t==nil)//如果是头节点,不对其进行删除操作        return;    t->prev->next=t->next;//以下为删除的两个步骤,请在纸上模拟这个过程    t->next->prev=t->prev;    free(t);//释放内存空间}int main(){    init();//初始化,以下代码与代码①思路相同    int n,m,i,k,p,t;    scanf("%d",&n);    insert(nil,1);    for(i=2;i<=n;i++)    {           scanf("%d%d",&k,&p);        a=search(k);        if(p==0)            a=a->prev;        insert(a,i);    }    scanf("%d",&m);    for(i=1;i<=m;i++)    {        scanf("%d",&t);        if(mark[t])            continue;        a=search(t);        deletenode(a);        mark[t]=1;    }    for(a=nil->next;a!=nil;a=a->next)        printf("%d ",a->data);    printf("\n");    return 0; }

AC代码③(用数组模拟双向链表)

struct node{    int prev;    int next;};node list1[100001];//结构体数组list1int mark[100001];//标记数组int main(){    int n,m,i,j,k,p,t,cnt=0;//cnt记录被去掉的同学的人数    scanf("%d",&n);    list1[0].next=1;//将编号为1的同学加入链表    for(i=2;i<=n;i++)    {           scanf("%d%d",&k,&p);        if(p==0)//将编号为i的同学加入到编号为k的同学的左边        {            list1[i].next=k;//注意以下四个步骤不是任意顺序的,上一行代码可能会影响到下一行代码            list1[i].prev=list1[k].prev;            list1[list1[k].prev].next=i;            list1[k].prev=i;        }        else//将编号为i的同学加入到编号为k的同学的右边        {            list1[i].next=list1[k].next;            list1[i].prev=k;            list1[list1[k].next].prev=i;            list1[k].next=i;        }    }    scanf("%d",&m);    for(i=1;i<=m;i++)    {        scanf("%d",&t);        if(mark[t])            continue;        list1[list1[t].prev].next=list1[t].next;//删除节点        list1[list1[t].next].prev=list1[t].prev;        mark[t]=1;//标记        cnt++;//计数    }    int cur;    for(i=1;i<=n-cnt;i++)//循环次数为n-cnt    {        cur=list1[0].next;//从头节点指向的节点开始遍历链表        printf("%d ",cur);//打印    }    printf("\n");    return 0; }

算法描述:
此题不可以用单向链表实现。果然还是数组比较快。通过这道题,虽然WA了十几次很闹心,但是我练习到了STL、指针链表、数组链表。这个收获还是蛮丰富的。