力扣1462.课程表

题目描述:

你总共需要上 numCourses 门课,课程编号依次为 0 到 numCourses-1 。你会得到一个数组 prerequisite ,其中 prerequisites[i] = [ai, bi] 表示如果你想选 bi 课程,你 必须 先选 ai 课程。

  • 有的课会有直接的先修课程,比如如果想上课程 1 ,你必须先上课程 0 ,那么会以 [0,1] 数对的形式给出先修课程数对。

先决条件也可以是 间接 的。如果课程 a 是课程 b 的先决条件,课程 b 是课程 c 的先决条件,那么课程 a 就是课程 c 的先决条件。

你也得到一个数组 queries ,其中 queries[j] = [uj, vj]。对于第 j 个查询,您应该回答课程 uj 是否是课程 vj 的先决条件。

返回一个布尔数组 answer ,其中 answer[j] 是第 j 个查询的答案。

示例 1:

输入:numCourses = 2, prerequisites = [[1,0]], queries = [[0,1],[1,0]]
输出:[false,true]
解释:课程 0 不是课程 1 的先修课程,但课程 1 是课程 0 的先修课程。

示例 2:

输入:numCourses = 2, prerequisites = [], queries = [[1,0],[0,1]]
输出:[false,false]
解释:没有先修课程对,所以每门课程之间是独立的。
示例 3:

输入:numCourses = 3, prerequisites = [[1,2],[1,0],[2,0]], queries = [[1,0],[1,2]]
输出:[true,true]

提示:

  • 2 <= numCourses <= 100
  • 0 <= prerequisites.length <= (numCourses * (numCourses - 1) / 2)
  • prerequisites[i].length == 2
  • 0 <= ai, bi <= n - 1
  • ai != bi
  • 每一对 [ai, bi] 都 不同
  • 先修课程图中没有环。
  • 1 <= queries.length <= 104
  • 0 <= ui, vi <= n - 1
  • ui != vi

前言:

没错,又是课程表,只不过题目考的内容变了多了一点。理清题目的意思以及给的示例就要猜到这是一个有向图,又看到题目说没有环,那就容易想到拓扑图了。而且,题目中这种类似先决条件有强硬的顺序要求,容易考拓扑排序。

什么是拓扑排序?

对于任何有向图而言,其拓扑排序为其所有节点的一个线性排序(对于同一个有向图而言可能存在多个这样的节点排序)。该排序满足这样的条件:对于图中的任意两个节点u和v,若存在一条有向边从u指向v,则在拓扑排序中u一定出现在v前面。

举个例子:你的朋友来家里吃饭,你准备做饭,你要做饭,首先得买菜,买菜得去超市,去超市要出门搭车,因此这个顺序就是 出门搭车 -> 到超市 -> 买菜 -> 回家做饭

对于题目中的先决条件和间接条件也是一个道理,如果课程 a 是课程 b 的先决条件,课程 b 是课程 c 的先决条件,要想修课程c,那么就要先修课程b,而要想修课程吧,那么就要去修课程a。所以,修这三门课的拓扑顺序就是a-->b-->c,a一定在b的前面,b一定在c的前面。

思路:

由于题目要求的是对于第 j 个查询,回答课程 uj 是否是课程 vj 的先决条件。

我们可以建立一个邻接矩阵,f[a][b]表示a-->b这条边,返回类型为bool。f[a][b]表示a是b的先决条件。

那么在哪里去更新这个f[][]呢?

对于拓扑排序这个算法我们知道,每次在遍历一条边后都会把这条边删除,当遍历到t-->j这一条边,说明t是j的先决条件,也就是f[t][j]=true。那么我们可以再去遍历一遍所有到 j 的点,看看由f[t][j]能不能推出来其他的先决条件.


               f[t][j]=true;
               for(int h=0;h<numCourses;h++){
                   if(f[h][t]==true)f[h][j]=true;
               }

如果f[h][t]==true,说明h是t的先决条件,又由于f[t][j]=true,又能推出来f[h][j]=true.也就是当h-->t存在,t-->j存在,那么h-->j也存在。

由此,对于每一个点来说,每一个可以到这个点的边都可以被确认,这样一来,当我们把所有的边都遍历完后,可以确定的关系也就出来了。

代码:

public:
     int h[110],ne[10000],e[10000],idx;
     int d[110];
     int q[110];
     bool f[110][110];
     int hh=0,tt=-1;
     void add(int a,int b){
         e[idx]=b,ne[idx]=h[a],h[a]=idx++;
     }


    void topu(int numCourses){
       while(hh<=tt){
           int t=q[hh++];

           for(int i=h[t];i!=-1;i=ne[i]){
               int j=e[i];

               f[t][j]=true;
               for(int h=0;h<numCourses;h++){
                   if(f[h][t]==true)f[h][j]=true;
               }
               d[j]--;
               if(d[j]==0){
                   q[++tt]=j;
               }
           }
       }
    }
    vector<bool> checkIfPrerequisite(int numCourses, vector<vector<int>>& prerequisites, vector<vector<int>>& queries) {

       memset(h,-1,sizeof h);
       for(int i=0;i<numCourses;i++)d[i]=0;
       for(int i=0;i<10000;i++){
           ne[i]=0;
           e[i]=0;
       }
        for(auto it:prerequisites){
            int x=it[0];
            int y=it[1];
            add(x,y);
            d[y]++;
        }
        
        for(int i=0;i<numCourses;i++){
            if(d[i]==0){
                q[++tt]=i;
            }
        }
        topu(numCourses);
        vector<bool> ans;
       for(auto it:queries){
           int x=it[0];
           int y=it[1];
           
          ans.push_back(f[x][y]);
       }
        return ans;
    }

如果有对拓扑排序算法不了解的同学可以去b站的董晓算法去看看,讲的非常详细。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值