1. 关于拓扑排序
l 在我们所有可以选择的课程中,任意两门课程之间的关系要么是确定的(即拥有先后关系),要么是不确定的(即没有先后关系),绝对不存在互相矛盾的关系(即环路)。以上就是偏序的意义,抽象而言,有向图中两个顶点之间不存在环路,至于连通与否,是无所谓的。所以,有向无环图必然是满足偏序关系的。
l 所谓全序,就是在偏序的基础之上,有向无环图中的任意一对顶点还需要有明确的关系(反映在图中,就是单向连通的关系,注意不能双向连通,那就成环了)。可见,全序就是偏序的一种特殊情况。
2. 拓扑排序的算法:
2.1. 图不仅是数据结构,还是矩阵。好多都是用矩阵来表示的。
2.2. 简单的说,由某个集合上的一个偏序得到该集合上的一个全序,这个操作称之为拓扑排序
2.3. 通常,我们把这种顶点表示活动、边表示活动间先后关系的有向图称做顶点活动网(Activity On Vertex network),简称AOV网。
因为拓扑多用于这种活动的排序
2.4. 拓扑排序得到的结果并不唯一。也就是如果没有前置的点很多,那么他们之间不需要排序。
2.5. (百度百科)
queue<int>q;
//priority_queue<int,vector<int>,greater<int>>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);
}
}
}
}
2.6. 首先,q表示装下入度为0的点。然后再q不为空的情况下,每次去队列最前,同时改变与队列最前相关的点的入度,然后才把入度为0的点加入q。
2.7. 下面是伪代码:(维基百科)
L← Empty list that will contain the sorted elements
S ← Set of all nodes with no incoming edges
while S is non-empty do
remove a node n from S
insert n into L
foreach node m with an edge e from nto m do
remove edge e from thegraph
ifm has no other incoming edges then
insert m into S
if graph has edges then
return error (graph has at least onecycle)
else
return L (a topologically sortedorder)
2.7. 下面是dfs版本的,参考博客:
http://blog.csdn.net/dm_vincent/article/details/7714519
L ← Empty list that will contain the sorted nodes
S ← Set of all nodes with no outgoing edges
for each node n in S do
visit(n)
function visit(node n)
if n has not been visited yet then
mark n as visited
for each node m with an edgefrom m to ndo
visit(m)
add n to L
解释:从后往前,直到前面没有节点,说明到了最前,也就是入度为0.然后递归返回就可以了。
1. 本题需要注意的是分三种情况:也就是有环(矛盾),无法排序(也就是入度大于1),排序。所以要稍加变通,拓扑排序算法里面需要加上一个入度判断。
2. 当然还有一点,就是这道题最好每次输入都判断一下。这样可以剪枝了。所以这里使用了返回值来判断。
3. 整体的算法思想其实也很简单,就是根据map成图,然后记录每个点的入点度,如果入点度为1,就放入队列,如果为0,就有环。当然,排序完成了入点度肯定是0.但是这个不用担心,因为此处循环用节点数来控制。排序完成就不会再判断了。然后大于1,肯定无法排序。因为此处是每次输入就判断,也就是并没有完全成图,所以大于1就不是无法排序,只是再输入再排。直到所有节点输入完成。、
4. 入点度m大于1时的处理感觉很巧妙,就是不返回,因为还需要处理下面的点。但是标记为-1,但是main函数里面并没有标记为-1的处理。因为不能处理。只有确定没有环,没有解之后才能说不能排序。
5. 另外,因为此处直接是用字母表示,所以直接把index放入队列即可.
6. 后来有问题,发现是m的初始化有问题,要在for里面每一次都初始化。还是自己考虑不成熟。
7. 突然明白,scanf有点像java里面的扫描类,就是为扫描而生。
8. 232K 0MS
1. 本题解时参考了http://www.cnblogs.com/yueshuqiao/archive/2011/08/16/2140485.html
#include<iostream>
#include<stdio.h>
#include<string>
#include<string.h>
using namespace std;
const int maxs = 27;
int n,m;
int map[maxs][maxs];
int indegree[maxs];
bool flag;
int q[maxs];
int tuopu()
{
int temp[maxs];//因为indegree每次都要修改,所以就另赋值。
int sign;
sign = 1;//返回值
int m = 0;
int loc =0;
int c = 0;//每次都是一个新的队列
for(int i = 0; i < maxs; i++)
{
temp[i] = indegree[i];
}
for(int i = 0; i<n;i++)
{
m=0;
for(int j = 0; j < n; j++)
{
if(temp[j]==0)
{
loc = j;
// cout<<"测试loc: "<<loc<<endl;
m++;
}
}
//cout<<"测试m: "<<m<<endl;
if(m==0)return 0;
if(m>1)
{
sign = -1;//返回-1才是巧妙之处,他的作用就是标记没有排序成功,也没有环。
// return 0;此时还不能返回,因为还有其他点呢
}
q[c++] = loc;
temp[loc] = -1;
for(int j = 0; j < n; j++)
{
if(map[loc][j]==1)
temp[j]--;
}
}
return sign;
}
int main()
{
freopen("input.txt","r", stdin);
while(cin>>n>>m&&!(n==0&&m==0))
{
//cout<<"测试n,m: "<<n<<","<<m<<endl;
flag = 1;
char str[5];
memset(map, 0, sizeof(map));
memset(indegree,0,sizeof(indegree));
for(int i = 0; i<m; i++)
{
scanf("%s",str);
if(flag==0) continue;
int x = str[0]-'A';
int y = str[2]-'A';
map[x][y] = 1;
indegree[y]++;
int s = tuopu();
if(s==0)
{
cout<<"Inconsistency found after "<<i+1<<" relations."<<endl;
flag = 0;
// break;
}
else if(s==1)
{
cout<<"Sorted sequence determined after "<<i+1<<" relations: ";
for(int i = 0; i<n; i++)
{
//cout<<endl;
// cout<<"测试4:q: "<<q[i]<<endl;
printf("%c",q[i]+'A');
}
cout<<"."<<endl;
flag = 0;
// break;
}
}
if(flag)
{
cout<<"Sorted sequence cannot be determined."<<endl;
}
}
}