博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

计算拓扑序列

Posted on 2010-10-20 11:03  桃子在路上  阅读(4523)  评论(0编辑  收藏  举报

所谓拓扑序列,就是有向图的最长路径问题,如果图中存在环,则最长路径是无法求得的,所以有拓扑序列的有向图不可以存在环。具体定义如下:
    给出有向图G=(V,E),若结点的线形序列V1,V2,...Vn满足条件:对于i,j(1≤j<i≤n),Vi和Vj之间没有边。求线形序列V1,V2,...Vn的过程就称为拓扑排序,这个线形序列就称为拓扑序列。
【拓扑排序主要思想】
 有向图可以拓扑排序的条件是:图中没有环。
 具体方法:
 ⑴ 从图中选择一个入度为0的点加入拓扑序列。
 ⑵ 从图中删除该结点以及它的所有出边(即与之相邻点入度减1)。
 反复执行这两个步骤,直到所有结点都已经进入拓扑序列。

【实例:士兵排队问题】
有n个士兵(1≤n≤26),依次编号为A,B,C,...,队列训练时,指挥官要把一些士兵从高到矮排成一行。但现在指挥官不能直接获得每个人的身高信息,只能获得“p1比p2高”这样的比较结果,记作(p1>p2)。例如A>B,B>D,F>D,对应的排队方案有三个:AFBD,FABD,ABFD
【输入】
    k行,每行a b,表示a>b
【输出】
    一个可行的排队方案


【输入样例】
A B
B D
F D 
 
【输出样例】
ABFD
                                    
 

program soldier_sort;
var
  w:array['A'..'Z','A'..'Z'] of 0..1;
  d:array['A'..'Z'] of integer;         {记录顶点入度的数组}
  s:set of 'A'..'Z';
  a,b,ch:char;
  m,n:string;
  i,j,k:integer;
begin
  assign(input,'tuopu.in');
  reset(input);
  assign(output,'tuopu.out');
  rewrite(output);
  s:=[];
  while not eof(input) do
    begin
     readln(a,ch,b);
     s:=s+[a,b];        {计算士兵名集合}
     w[a,b]:=1;
     d[b]:=d[b]+1;      {累计顶点b的入度}
    end;
  m:='';
  for a:='A' to 'Z' do
  if a in s
    then m:=m+a;        {产生士兵名字符集}
  k:=length(m);         {求得士兵人数}
  n:='';                {拓扑序列初始为空}
  for i:=1 to k do
    begin
     j:=1;
     while (d[m[j]]>0) and (j<=k) do   {搜索第i个入度为0的士兵的顶点序号j}
         j:=j+1;
     if j>k                            {若不存在入度为0的顶点,则无法拓扑排序失败}
       then begin
             writeln('Fault!'); 
             break;
            end;
     n:=n+m[j];         {入度为0的顶点进入拓扑序列n}
     a:=m[j];           {删去顶点j}
     d[a]:=maxint;
     for j:=1 to k do   {与a相连的顶点入度减1}
     if w[a,m[j]]>0
        then d[m[j]]:=d[m[j]]-1;
   end;{for}
  writeln(n);
  close(input);
  close(output);
end.