Rank of Tetris(并查集和拓扑排序)

Problem Description
自从Lele开发了Rating系统,他的Tetris事业更是如虎添翼,不久他遍把这个游戏推向了全球。

为了更好的符合那些爱好者的喜好,Lele又想了一个新点子:他将制作一个全球Tetris高手排行榜,定时更新,名堂要比福布斯富豪榜还响。关于如何排名,这个不用说都知道是根据Rating从高到低来排,如果两个人具有相同的Rating,那就按这几个人的RP从高到低来排。

终于,Lele要开始行动了,对N个人进行排名。为了方便起见,每个人都已经被编号,分别从0到N-1,并且编号越大,RP就越高。
同时Lele从狗仔队里取得一些(M个)关于Rating的信息。这些信息可能有三种情况,分别是"A > B","A = B","A < B",分别表示A的Rating高于B,等于B,小于B。

现在Lele并不是让你来帮他制作这个高手榜,他只是想知道,根据这些信息是否能够确定出这个高手榜,是的话就输出"OK"。否则就请你判断出错的原因,到底是因为信息不完全(输出"UNCERTAIN"),还是因为这些信息中包含冲突(输出"CONFLICT")。
注意,如果信息中同时包含冲突且信息不完全,就输出"CONFLICT"。
 
Input
本题目包含多组测试,请处理到文件结束。
每组测试第一行包含两个整数N,M(0<=N<=10000,0<=M<=20000),分别表示要排名的人数以及得到的关系数。
接下来有M行,分别表示这些关系
 
Output
对于每组测试,在一行里按题目要求输出
 
Sample Input
3 3
0 > 1
1 < 2
0 > 2
4 4
1 = 2
1 > 3
2 > 0
0 > 1
3 3
1 > 0
1 > 2
2 < 1
 
Sample Output
OK
CONFLICT
UNCERTAIN
 
Author
linle
 
Source
HDOJ 2007 Summer Exercise(2)
 
Recommend
lcy
 

自己第一次接触拓扑排序问题,感觉还是不太明白,所以借鉴了一位大佬的代码,首先,

(1)首先解决这道题用到的基础知识要先明白,就是并查集和拓扑排序
(2)关于并查集是用来处理=的情况的,当两个元素相等时,需要合并集合。
(3)拓扑排序,用来判断信息是否完整,是否出现冲突。
 a)信息不完整的表现是同时出现两个或两个以上的元素,其入度为0,且其根是自己(因为有可能是=合并后造成的情况)。
 b)出现冲突的表现是出现环了,这样就会造成拓扑排序的元素少于需要排序的元素,有的元素永远没有进行排序,是一个死循环的环了。
下面简介一下拓扑排序(摘自百度百科):
对一个 有向无环图(Directed Acyclic Graph简称DAG)G进行拓扑排序,是将G中所有顶点排成一个线性序列,使得图中任意一对顶点u和v,若边(u,v)∈E(G),则u在线性序列中出现在v之前。通常,这样的线性序列称为满足拓扑次序(Topological Order)的序列,简称拓扑序列。简单的说,由某个集合上的一个 偏序得到该集合上的一个 全序,这个操作称之为拓扑排序。
一个较大的工程往往被划分成许多子工程,我们把这些子工程称作 活动(activity)。在整个 工程中,有些子工程(活动)必须在其它有关子工程完成之后才能开始,也就是说,一个子工程的开始是以它的所有前序子工程的结束为先决条件的,但有些子工程没有先决条件,可以安排在任何时间开始。为了形象地反映出整个工程中各个子工程(活动)之间的先后关系,可用一个有向图来表示,图中的顶点代表活动(子工程),图中的有向边代表活动的先后关系,即有向边的起点的活动是终点活动的前序活动,只有当起点活动完成之后,其终点活动才能进行。通常,我们把这种顶点表示活动、边表示活动间先后关系的有向图称做 顶点活动网(Activity On Vertex network),简称 AOV网。
Rank of Tetris(并查集和拓扑排序)_第1张图片
例如,假定一个计算机专业的学生必须完成图3-4所列出的全部课程。在这里,课程代表活动,学习一门课程就表示进行一项活动,学习每门课程的先决条件是学完它的全部先修课程。如学习《数据结构》课程就必须安排在学完它的两门先修课程《离散数学》和《算法语言》之后。学习《高等数学》课程则可以随时安排,因为它是基础课程,没有先修课。若用AOV网来表示这种课程安排的先后关系,则如图3-5所示。图中的每个顶点代表一门课程,每条有向边代表起点对应的课程是终点对应课程的先修课。图中的每个顶点代表一从图中可以清楚地看出各课程之间的先修和后续的关系。如课程C5的先修课为C2,后续课程为C4和C6。 [2]  
一个AOV网应该是一个 有向无环图,即不应该带有回路,因为若带有回路,则回路上的所有活动都无法进行。如图3-6是一个具有三个顶点的 回路,由边可得B活动必须在A活动之后,由边可得C活动必须在B活动之后,所以推出C活动必然在A活动之后,但由边可得C活动必须在A活动之前,从而出现矛盾,使每一项活动都无法进行。这种情况若在程序中出现,则称为死锁或死循环,是应该必须避免的。
在AOV网中,若不存在回路,则所有活动可排列成一个线性序列,使得每个活动的所有前驱活动都排在该活动的前面,我们把此序列叫做 拓扑序列(Topological order),由 AOV网构造拓扑序列的过程叫做 拓扑排序(Topological sort)。AOV网的拓扑序列不是唯一的,满足上述定义的任一线性序列都称作它的拓扑序列。
由AOV网构造出拓扑序列的实际意义是:如果按照拓扑序列中的顶点次序,在开始每一项活动时,能够保证它的所有前驱活动都已完成,从而使整个工程顺序进行,不会出现冲突的情况。

拓扑序列 C++(STL)核心代码

queueq;
//priority_queue,greater>q;
//优先队列的话,会按照数值大小有顺序的输出
//此处为了理解,暂时就用简单队列
inttopo()
{
for(inti=1;i<=n;i++)
{
if(indegree[i]==0)
{
q.push(i);
}
}
 
inttemp;
while(!q.empty())
{
temp=q.front();//如果是优先队列,这里可以是top()
printf("%d->",temp);
q.pop();
for(inti=1;i<=n;i++)//遍历从temp出发的每一条边,入度--
{
if(map[temp][i])
{
indegree[i]--;
if(indegree[i]==0)q.push(i);
}
}
}
}
此题使用数组和结构体,来模拟了临界表和queue来实现的拓扑排序。

代码L

#include 
#include 
#include 
#include 
#define MAX_NUM 100010

using namespace std;

//该结构体用来存储 比某个值小的节点,模仿临界表后边的链表
typedef struct Node
{
    int num;//数值
    Node *next;
}Node;

//用来模仿临界表的数组
struct
{
    int rd;//入度
    Node *next;
}vect[MAX_NUM];

// N、M 分别表示要排名的人数以及得到的关系数
int N,M;
int sum;//用来记录总元素个数
int A[MAX_NUM],B[MAX_NUM];
char oper[MAX_NUM];//比较运算符
//根,用来判断是否为一个序列
int root[MAX_NUM];
//拓扑排序中使用,用来模仿临界表,这里的next用来记录比他大的所有的元素

int que[MAX_NUM],front,rear;//模拟队列

//找到根
int find_root(int a)
{
    if(root[a] == a)return a;
    return root[a] = find_root(root[a]);
}

//合并两个集合
int union_set(int a,int b)
{
    a = find_root(a);
    b = find_root(b);
    if(a==b)return 0;
    root[b]=a;
    return 1;
}
//初始化
void init()
{
    memset(vect,0,sizeof(vect));
    for(int i=0;inum=b;
    no->next = vect[a].next;
    vect[a].next = no;
    vect[b].rd++;
}

//交换两个元素
void swap(int &a,int &b)
{
    int tem = a;
    a = b;
    b = tem;
}

//拓扑排序
void top_order()
{
    bool uncertain=false;
    //将入度为0,且是根的节点放入队列中,
    //若队列中的结点个数大于,则说明信息不全
    for(int i = 0;i < N;i++)
    {
        if(vect[i].rd==0&&find_root(i)==i)
        que[rear++] = i;//入队,同时尾指针加1
    }
    while(front!=rear)
    {
        if(rear-front>1)//出现信息不全的情况
        uncertain = true;
        int cur = que[front++];
        sum--;
        for(Node *i=vect[cur].next;i!=NULL;i=i->next )
        {
            if(--vect[i->num].rd==0)
            que[rear++]=i->num;
        }
    }
    if(sum>0)printf("CONFLICT\n");
    else if(uncertain)printf("UNCERTAIN\n");
    else printf("OK\n");
}

int main()
{
    int a,b;
    while(scanf("%d%d",&N,&M)!=EOF)
    {
        init();
        sum = N;
        for(int i=0;i



malloc函数

malloc 向系统申请分配指定size个字节的内存空间。返回类型是 void* 类型。void* 表示未确定类型的指针。C,C++规定,void* 类型可以通过类型转换强制转换为任何其它类型的指针。

函数定义

编辑

原型

1
extern  void  * malloc (unsigned  int  num_bytes);

头文件

1
2
3
#include 
或者
#include 

函数声明

1
void  * malloc ( size_t  size);
备注:void* 表示未确定类型的 指针 ,void *可以指向任何类型的数据,更明确的说是指申请内存空间时还不知道用户是用这段空间来存储什么类型的数据(比如是char还是 int 或者其他数据类型)。

功能

分配长度为num_bytes字节的内存块

返回值

如果分配成功则返回指向被分配内存的 指针(此存储区中的初始值不确定),否则返回空指针 NULL。当内存不再使用时,应使用free()函数将内存块释放。函数返回的指针一定要适当对齐,使其可以用于任何 数据对象。

说明

关于该函数的原型,在以前malloc返回的是 char型 指针,新的ANSIC标准规定,该函数返回为 void型指针,因此必要时要进行类型转换。

名称解释

malloc的全称是memory allocation,中文叫动态内存分配,当无法知道内存具体位置的时候,想要绑定真正的内存空间,就需要用到动态的分配内存。

相关函数

calloc、 realloc、 free、 _alloca

free是一个英文词语,基本意思是自由的;不受控制的。

malloc是动态分配空间的意思。
一般用法
int *t=NULL;
t=(int *)malloc(sizeof(int));
也可以在sizeof前面加上一个'n*'这就成了一个动态分配数组的方法。
free是释放空间的意思。
一般用法:
int *t=NULL;
t=(int *)malloc(sizeof(int));
free(t);
这样t所指的空间就被释放掉了。

你可能感兴趣的:(ACMSTEPS)