今学期はnumCourseの科目を履修する必要があり、numCourse-0からnumCourse-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 <= numCourses <=
推論
- 前提条件が依存関係のセットであることを考慮すると、複数のサブセットが含まれる可能性があります。
- この方法では、隣接していない前提条件の部分集合、つまり[1,0,2]における1,2のアウト度とイン度はうまくカウントされません。
問題の公式解答を読んで、前提条件より多くの制約が2つだけの要素、その統計のうち程度の単純さに:
- numCoursesに含まれるコースのうち、依存関係を持つコースについては、その依存関係
- 各要素のイン度数とアウト度数のサブセットのカウント
- ある要素のエントリー数が0である場合、その要素はエントリーとして選択できることを意味します。
- そのout-degreeの部分集合を繰り返し、程度にサブセットが1つずつ削減され、つまり、列挙選択的なそれら
- エントリ番号0の最後の要素を列挙することは、列挙を試みるごとにコースが受講されたことを示します。
- すべてのクラスが履修済みであれば真を返します。
/**
* @param {number} numCourses
* @param {number[][]} prerequisites
* @return {boolean}
*/
var canFinish = function (numCourses, prerequisites) {
let mapItem = new Map(), // アウト度の部分集合
mapNum = new Map(), // アクセス接続数
_result = numCourses,
startList = [],
len = prerequisites.length;
// 初始化入度连接数 与 出度子集
for (let i = 0; i < numCourses; i++) {
mapItem.set(i, []);
mapNum.set(i, 0);
}
// イン・ディグリーとアウト・ディグリーのデータを埋める
for (let i = 0; i < len; i++) {
// prerequisites[1] 需要在 prerequisites[0]
// before -> after
// before -> after (
let after = prerequisites[i][0],
before = prerequisites[i][1],
afterValue = mapNum.get(after),
beforeValue = mapItem.get(before);
mapItem.set(before, [...beforeValue, after])
mapNum.set(after, afterValue + 1)
}
// 他のクラスに依存しない要素を取り出す
for (let [key, value] of mapNum) {
if (value === 0) startList.push(key);
}
while (startList.length) {
// 未学習クラスに依存しない選択コースの要素の列挙
let item = startList.shift(),
nextItem = mapItem.get(item);
// コース選択マイナス1の列挙ラウンド
_result--;
if (nextItem && nextItem.length) {
// このラウンドのすべてのコース選択の可能性を試み、選択後にエントリーが0となる要素に遭遇する。
// 次に、列挙リストを進め、後で以下の線でコースを選択してみる。
for (let i = 0; i < nextItem.length; i++) {
let nextNum = mapNum.get(nextItem[i]) - 1;
mapNum.set(nextItem[i], nextNum);
if (nextNum === 0) {
startList.push(nextItem[i]);
}
}
}
}
// すべてのコースが選択されているか
return _result === 0;
}
深さ優先探索
- 各コースの前提条件を記録
- 選択コースを繰り返し、1とマークされたコースを選択し、その従属コースを繰り返し選択します:
- 先行コースが選択されていない場合、この先行コースの選択に変換します。
- 選択可能な先行コースがあり、もはや依存関係がない場合、2をマークします。
- 前任者コースにも1がマークされた場合、次のようになります: このコースの前任者コースは、非依存性を満たすことができない前任者コースも持ち、進入度のない閉ループを形成します。
/**
* @param {number} numCourses
* @param {number[][]} prerequisites
* @return {boolean}
*/
var canFinish = function(numCourses, prerequisites) {
let map = new Map(),
valid = true,
visited = Array(numCourses).fill(0);
for (let i = 0; i < numCourses; i++) {
map.set(i, [])
}
// 查询每个课程的的前置课程 即可能的出度子集
for (let i = 0; i < prerequisites.length; i++) {
let after = prerequisites[i][0],
before = prerequisites[i][1],
beforeValue = map.get(before);
map.set(before, [...beforeValue, after])
}
// 循环为被学习的课程检修是否存在 闭环
for (let i = 0; (i < numCourses) && valid; i++) {
if (visited[i] == 0) {
dfs(i);
}
}
// 入力コースマーカー選択肢1
function dfs(u) {
visited[u] = 1;
for (let v of map.values) {
// 前提科目が選択されていない場合は、まず前提科目を選択する。
if (visited[v] === 0) {
dfs(v);
if (!valid) {
return;
}
} else if (visited[v] === 1) {
// 先行要素も1と表示されている場合、uを探している先行コースvは、vの先行コースも同様に未取得の先行コースに依存していることを意味する
valid = false;
return;
}
}
// 前提条件コースの照会を完了すると、そのコースは完了したことになる 2
visited[u] = 2;
}
// デフォルト成功、閉ループが存在する場合は失敗
return valid;
};
ブログ:
出版:The Pit's Little Bookie