1. 程式人生 > >LeetCode207 課程表

LeetCode207 課程表

問題:課程表

現在你總共有 n 門課需要選,記為 0 到 n-1

在選修某些課程之前需要一些先修課程。 例如,想要學習課程 0 ,你需要先完成課程 1 ,我們用一個匹配來表示他們: [0,1]

給定課程總量以及它們的先決條件,判斷是否可能完成所有課程的學習?

示例 1:

輸入: 2, [[1,0]] 
輸出: true
解釋: 總共有 2 門課程。學習課程 1 之前,你需要完成課程 0。所以這是可能的。

示例 2:

輸入: 2, [[1,0],[0,1]]
輸出: false
解釋: 總共有 2 門課程。學習課程 1 之前,你需要先完成​課程 0;並且學習課程 0 之前,你還應先完成課程 1。這是不可能的。

說明:

  1. 輸入的先決條件是由邊緣列表表示的圖形,而不是鄰接矩陣。詳情請參見圖的表示法
  2. 你可以假定輸入的先決條件中沒有重複的邊。

提示:

  1. 這個問題相當於查詢一個迴圈是否存在於有向圖中。如果存在迴圈,則不存在拓撲排序,因此不可能選取所有課程進行學習。
  2. 通過 DFS 進行拓撲排序 - 一個關於Coursera的精彩視訊教程(21分鐘),介紹拓撲排序的基本概念。
  3. 拓撲排序也可以通過 BFS 完成。

連結:https://leetcode-cn.com/problems/course-schedule/description/

分析:

1,一門課的先修課可能有多個,只要有一個沒學就沒法學

2,如果進行一輪後,沒有新增學到的課,那麼後續也不會再增加了

3,如果學到的課就是課程數量,則學了所有的,否則學不完。

所以可以設定如下資料結構:

class Class
{
public:
    int Num=INT_MAX; //為止
    int Status=0; // 未學習
    vector<int> dps;
};

Num是課程編號,Status是課程狀態,0表示沒學,1表示學了,dps表示以來課程。

可以假設所有的課程都不依賴任何課,初始化完成後,根據依賴關係更新,然後找到不依賴其他課的課程,

以沒有先修課的課程為基礎,持續更新, 直到不再新增學到的課程位置。

更新方式為:如果一個課程的先修課都已經學過了,這門課也可以學習。

 

AC Code:

 

class Class
{
public:
    int Num=INT_MAX; //為止
    int Status=0; // 未學習
    vector<int> dps;
};
class Solution {
public:
    
    bool canFinish(int numCourses, vector<pair<int, int>>& prerequisites) {
        bool ret = false;
        //如果能找到一個起始課程,即不依賴任何課程的,從這個開始學習所有
        //如果找不到,則不能進行
        vector<Class> classes;
        for (int i = 0; i < numCourses; i++)
        {
            Class tmp;
            tmp.Num = i;
            tmp.Status = 0;
            tmp.dps = vector<int>();
            classes.emplace_back(tmp);
        }
        int n = prerequisites.size();
        sort(prerequisites.begin(), prerequisites.end());
        for (int i = 0; i <n; i++)
        {
            int tmpnum = prerequisites[i].first;
            int depnum = prerequisites[i].second;
            for (int j = 0; j < classes.size(); j++)
            {
                if (classes[j].Num == tmpnum)
                {
                    classes[j].dps.emplace_back(depnum);    
                    break;
                }
            }
        }

        //找到所有的不依賴其他的課程,並且從哪些出發得打所有的不依賴的課程
        int learned = 0;
        vector<int> learnedclass;
        for (int i = 0; i < classes.size(); i++)
        {
            if (classes[i].dps.size() == 0)
            {
                classes[i].Status = 1;
                learned++;
                learnedclass.emplace_back(classes[i].Num);
            }
        }
        while (true)
        {
            int currentlearned = learned;
            for (int i = 0; i < classes.size(); i++)
            {
                if (classes[i].Status == 1)
                {
                    continue;
                }
                else
                {
                    //這門課沒學,看先修課有沒有學
                    vector<int> tmpre = classes[i].dps;
                    int learnflag = 1;
                    for (int j = 0; j < tmpre.size(); j++)
                    {
                        if (find(learnedclass.begin(), learnedclass.end(), tmpre[j]) == learnedclass.end())
                        {
                            learnflag = 0; //有先修課沒學,學不了了
                            break;
                        }
                    }
                    if (learnflag == 1)
                    {
                        //這門課可以學
                        classes[i].Status = 1;
                        learned++;
                        learnedclass.emplace_back(classes[i].Num);
                    }

                }
            }
            if (currentlearned == learned)
            {
                //沒有增加學習的課程,可以結束了
                //if(learned == )
                return learned == numCourses;
            }
        }



        return ret;
    }
};

 

其他:

1.QQ群裡有人提到這個題,周賽第四題沒搞懂dp狀態轉移方程,解決這個問題消磨時間吧。

提示中有提到圖論等知識,不過也就學資料結構的時候學過這個,平時不怎麼用,或者說不知道主動去用,沒用圖論的知識,雖然過了,效率比較低,以後有機會好好補補相關知識。

2.太冷動手,沒太大興趣看了,不過可以優化的地方有,一門課是否能學,看其先修課是否已經學過了,那麼下一次更新的時候,再次檢視就浪費時間,可以看缺了哪些課,這樣待處理的資料會逐漸減少避免重複運算。

3.用時最短code:

 1 static const auto _ = []()
 2 {
 3     ios::sync_with_stdio(false);
 4     cin.tie(nullptr);
 5     return nullptr;
 6 }();
 7 
 8 class Solution {
 9     vector<int> *vex;   //vex[a]={b,c}:要學習a,須先學b,c
10     char *visit;        //0:從未訪問,1:正在訪問,2:曾經訪問
11     //若成環,返回false
12     bool DFS(int vID){
13         if(visit[vID]==1)
14             return false;   //成環了
15         if(visit[vID]==0){  //0:從未訪問
16             visit[vID]=1;   //1:正在訪問
17             for(int i=0;i<vex[vID].size();++i){
18                 if(!DFS(vex[vID][i]))return false;  //成環了
19             }
20             visit[vID]=2;   //2:曾經訪問
21         }
22         return true;    //訪問過了
23     }
24 public:
25     bool canFinish(int numCourses, vector<pair<int, int>>& prerequisites) {
26         vex= new vector<int>[numCourses];
27         visit= new char[numCourses];
28         fill(visit,visit+numCourses,0);         //0:從未訪問
29         for(int i=0;i<prerequisites.size();++i){
30             auto &edge=prerequisites[i];
31             vex[edge.first].push_back(edge.second);  //[1,0],學習課程 1 之前,你需要完成課程 0。
32         }
33         //逐個訪問
34         for(int i=0;i<numCourses;++i){
35             if(!DFS(i))return false;
36         }
37         return true;   //沒有成環即可
38     }
39 };