uva 540 Team Queue
题意加算法描述:先给出T个团体,并给出每个团体有多少人,每个人的编号,然后所有团体一起排队,排成一条大队列,排队的原则是,一个成员加入,如果这个成员所在的团体已经有人在排队了,那么他就加到他所在团体的最后面,而不是整个大队列的最后。如果整个大队列中没有他的团体,也就是他是他的那个团体第一个来的人,那么他就要排在整个大队列的最后(当然,他成为了他这个团体的第一人,以后他的队友来了就可以排他后面)
出队则是按大队列的顺序,从第一名开始出队的,也就是说,按排在前面的团体的人按顺序逐一出完队后下一个团体的人才有可能出队
本题在写的过程中运用了大量映射的思想,并且本题本身是个队列数据结构的题目,且用到了两个队列:团体在大队列里是排队,而团体内部的成员也是 在排队的。题目中提示了本题的入队和出队操作都必须是常数阶,否则会超时,所以队列的结构有讲究,绝对不会用遍历的方式来实现入队和出队
重点:1.大量运用了映射。2团体的排名用了循环队列结构,这样才能避免遍历。3.团体内部的成员排队运用了链表队列的结构,这样才能避免遍历
4.出队入队会有一些特殊情况,注意指针的使用,就是指针的细节导致本题多次RE
5.循环队列的运用无非是front和rear的指向问题,同样是很重要的细节问题;复习 一下循环队列,本题中有T个团体,那个循环队列的长度应该为T+1,即下标0到T,空队列是front=rear=0,也就是程序一开始初始化的时候,已经所有人都出队了,整个大队列变回无人状态。front和rear向下一位移动不能简单的++,而应该是front=(front+1)%(T+1);rear=(rear+1)%(T+1); 注意是T+1
另外注意循环队列中rear永远指向最后一个数据的下一位,如果数据在最后一位了,那么rear=0,即循环回去,判断循环队列为空的条件是front=rear,而为满的条件是(rear+1)% Queuesize == front 。再强调一遍,在本题中,有T个团体,所以队列长度及Queuesize=T+1,下标0到T
#include <stdio.h> #include <string.h> #include <stdlib.h> #define MAX 1000010 #define LEN1 sizeof(struct queue) #define LEN2 sizeof(struct node) int element[MAX]; //题中说任何一个元素的大小都不会超过999999,所以没输入一个数字就记录它是属于那个团体的 int team[1010]; //下标对应的就是团体编号,team[i]表示i团体在总队列的位置 struct queue { int num; //记录是哪个团体 struct node *front,*rear; //连接这个团体的所有元素 }q[1010]; //题目说最多有1000个团体 int front,rear; //用于q队列的,q是一个循环队列 struct node { int data; struct node *next; }; void create_queue(int T) //有T个团体,要初始化T个位置 { int t; for(t=0; t<=T; t++) { q[t].num=-1; q[t].front=q[t].rear=(struct node*)malloc(LEN2); q[t].front->data=0; q[t].front->next=NULL; } front=rear=0; memset(team , -1 , sizeof(team)); } void ENQUEUE(int s,int T) { int num,k; struct node *temp; num=element[s]; //记录s成员是num团体的 temp=(struct node*)malloc(LEN2); temp->data=s; temp->next=NULL; if(team[num]==-1) //这个团体还没有人 { q[rear].num=num; team[num]=rear; //注意这些细节 rear=(rear+1)%(T+1); } k=team[num]; q[k].rear->next=temp; q[k].rear=temp; q[k].front->data++; } void DEQUEUE(int T) { struct node *temp; int k; temp=q[front].front->next; printf("%d\n",temp->data); q[front].front->data--; q[front].front->next=temp->next; free(temp); if( !q[front].front->data ) { k=q[front].num; team[k]=-1; q[front].rear=q[front].front; //多次RE就是因为这里,别忘了这个结点处已经没有数据,把q[front].rear回指 if((front+1)==rear) front=rear=0; //这个判断也比较重要,意思是删除这个后,整个大队列恢复到无人状态,那么就回指 else front=(front+1)%(T+1); } } void test_print_queue(int T) //这个函数只是为了检验而输出,不是程序所需 { int t; struct node *temp; printf("front=%d rear=%d\n",front,rear); if(front<rear) { for(t=front; t<rear; t++) { temp=q[t].front->next; printf("team:%d people=%d ",q[t].num , q[t].front->data); while(temp) {printf("%d ",temp->data); temp=temp->next; } printf("\n"); } } else { for(t=front; t<=T; t++) { temp=q[t].front->next; printf("team:%d people=%d ",q[t].num , q[t].front->data); while(temp) {printf("%d ",temp->data); temp=temp->next; } printf("\n"); } for(t=0; t<rear; t++) { temp=q[t].front->next; printf("team:%d people=%d ",q[t].num , q[t].front->data); while(temp) {printf("%d ",temp->data); temp=temp->next; } printf("\n"); } } for(t=1; t<=T; t++) printf("team(%d)=%d ",t,team[t]); printf("\n"); } int main() { int k=0,t,T,i,n,temp; char string[10]; while(scanf("%d",&T)!=EOF && T) //有T个团体 { k++; printf("Scenario #%d\n",k); for(t=1; t<=T; t++) { scanf("%d",&n); for(i=1; i<=n; i++) { scanf("%d",&temp); element[temp]=t; } //输入第t个团体有n个人,意义不大,然后输入这个n个人的标号,那么这n个人都是属于t团体的,把他们的团体编号记录下来 //最重要的是element[temp]=t; } getchar(); create_queue(T); //先创建一个总的队列,总队列中只有头结点,没有任何元素 while(scanf("%s",string)!=EOF && strcmp(string , "STOP")) { if(!strcmp(string , "DEQUEUE")) { DEQUEUE(T); // test_print_queue(T); //检验输出,输出所有团体包含的元素 } else { scanf("%d",&temp); ENQUEUE(temp,T); // test_print_queue(T); //检验输出,输出所有团体包含的元素 } } printf("\n"); } return 0; }