• 拓扑排序 图


    题目来源:

    https://hihocoder.com/problemset/problem/1175

    http://acm.hdu.edu.cn/showproblem.php?pid=2647

    第一题是一个最基本的拓扑排序,第二题可以理解为第一题的逆向思维。那么从第一题开始说起。

    在此之间先了解几个概念

    有向无环图

    比如a可以到b但是b不能到a,那么我们称a到b是有向的,相反的a和b之间可以互相抵达则是无向的,无向图在并查集的有涉及到。

    这里的第一题是保证数据是有向无环图的,第二题不保证,怎么处理后面细说。

    vecter

    这题是我第一用vector,关于vector怎么使用会在代码中注释出来,这里就不细说。

     说完了这些来看第一题病毒的传播,这一题要怎么实现题目都已经说了,这里简单说一下,给出病毒传输的路径,病毒不会逆向传播,从开端开始,把所有的开端压入队列,之后对开端能达到的病毒进行遍历,统计他们的病毒数目,并且每次将入度(即为有几个点能到达这个点)为0的病毒压入队列,直到遍历完所有的病毒位置。

    下面通过代码来细说

    #include<iostream>
    #include<queue>
    #include<vector>
    #define m 142857
    using namespace std;
    queue<int> q;//创建队列用来遍历点
    vector<int> vec[100005];//创建数组用来存该点可以到哪几个点
    int num[100005];//用来存该点的病毒数目
    int tree[100005];//存该点的入度
    int main()
    {
        int N,M,K,a,b,cnt=0;
        cin>>N>>M>>K;
         for(int i=1;i<=K;i++)
         {
             cin>>a;
             num[a]++;
         }
         for(int i=1;i<=M;i++)
         {
             cin>>a>>b;
             vec[a].push_back(b);//a能到b,所以将b压入a数组
             tree[b]++;//b的入度加一
         }
         for(int i=1;i<=N;i++)//找到所有的起点即入度为0的点
            if(tree[i]==0)
            q.push(i);
         while(!q.empty())//开始对每一个数据进行处理
         {
             a=q.front();
             q.pop();
             cnt+=num[a];//统计总共的病毒数
             cnt%=m;
             for(int i=0;i<vec[a].size();i++)//对a能到达的每一个点进行处理
             {
                 b=vec[a][i];
                 num[b]+=num[a];//统计病毒数
                 tree[b]--;//入度减一
                 num[b]%=m;
                 if(tree[b]==0)//入度为零,压入队列
                    q.push(b);
             }
         }
         cout<<cnt%m;
        return 0;
    }

     这是第一题,下面来看第二题,先说下题意一个分配工资的问题,题目给出所有人物之间的比较关系,要求输出最少的工资,若不满足所有人的要求,则输出-1。

    那么这一题跟第一题很类似,但是又有几个不同的地方,下面我举例子来进行说明,第一个是什么时候输出-1。

    看这个例子,3要比2多,4要比3多,2要比4多,很显然不成立,所以输出-1,那么也就说明了一旦成环,就不成立。在程序里如何判断就是根据有没有遍历全部的数,我们可以看出起点只有1,但是当1处理到2的时候,2的入度为2,减一后为1还是不为零,不会进入队列,所以程序到此结束,那么2,3,4都没有遍历,根据环的特性我们可以知道拓扑排序的方法是不会进入环的,那么我们只要记录一共处理了多少个点,和总的点判断就可以了。

    还有一个不同的地方

    这个例子里,2要比1多,3要比1多,那么假设1需要的工资为0的话,如果按照第一的方式,那么2和3需要的工资就为1,那么需要的总工资为2,这显然是错误的,正确的结果应该是2和3需要的为0,而1需要的工资为1,总工资为1,所以这一题就要反向建图,这是第二个不同点。

    下面看完整代码

    #include<iostream>
    #include<vector>
    #include<queue>
    #include<stdio.h>
    #include<cstring>
    using namespace std;
    int n,m,num,ans;
    vector<int> vec[10005];
    queue<int> q;
    int main()
    {
        int a,b;
        int tree[10005];
        int cnt[10005];//假设最小的工资为0,在此基础上每当需要增加的时候就加一
        while(scanf("%d",&n)!=EOF)
        {
            cin>>m;
            ans=888*n;//先计算出所有人的基本工资,方便后期计算。
            //因为是不定组输入,所以这里每一组数据都需要初始化
            memset(tree,0,sizeof(tree));//初始化
            memset(cnt,0,sizeof(cnt));//初始化
            for(int i=0;i<=10004;i++)//初始化
            {
                vec[i].clear();
            }
            num=0;
            for(int i=1;i<=m;i++)
            {
                cin>>a>>b;
                vec[b].push_back(a);//注意,这里是反向,把a设为b能到达的点
                tree[a]++;//a的入度加一
            }
            for(int i=1;i<=n;i++)//统计所有的起点
                if(tree[i]==0)
                {
                    q.push(i);
                    num++;
                }
            while(!q.empty())//处理所有的数据
            {
                a=q.front();
                q.pop();
                for(int i=0;i<vec[a].size();i++)
                {
                    b=vec[a][i];
                    cnt[b]=cnt[a]+1;//因为b工资要比a多,所以在a的基础上加一
                    tree[b]--;//b的入度减一
                    if(tree[b]==0)//若入度为零,就压入队列
                    {
                        q.push(b);
                        num++;
                    }
                }
            }
            if(num!=n)//查是不是搜索到了所有的点,若没有搜索到,则说明存在成环的情况
                cout<<"-1"<<;
            else
            {
                for(int i=1;i<=n;i++)//遍历所有的点,将所有需要增加的工资加初始工资里
                {
                    ans+=cnt[i];
                }
                cout<<ans<<endl;
            }
        }
        return 0;
    }
  • 相关阅读:
    第61课 智能指针类模板
    第60课 数组类模板
    第59课 类模板深度剖析
    第58课 类模板的概念和意义
    第57课 深入理解函数模板
    第56课 函数模板的概念和意义
    第55课 经典问题解析(四)
    155. Min Stack
    141. Linked List Cycle
    136. Single Number
  • 原文地址:https://www.cnblogs.com/zlhdbk/p/10775804.html
Copyright © 2020-2023  润新知