每日一题(7),poj1094,

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;
        }
    }
}





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值