Info

// https://leetcode.com/problems/parallel-courses-ii/description/
 
#include <bit>
#include <cstdint>
#include <iostream>
#include <optional>
#include <print>
#include <utility>
#include <vector>
 
template<typename Callback>
void iterateKSetBits(uint64_t bitMask, int k, Callback&& callback) {
    uint32_t subMask = 0;
    while (k--) {
        subMask |= std::bit_floor(bitMask ^ subMask);
    }
 
    auto prevStart = subMask;
 
    while (true) {
        callback(subMask);
 
        if (bitMask == subMask) {
            return;
        }
 
        auto a = (subMask & -subMask);
        auto b = std::bit_floor(bitMask & (a - 1));
 
        if (b) {
            subMask ^= a ^ b;
        } else {
            auto c = std::bit_floor(bitMask);
            bitMask ^= c;
            subMask = prevStart ^ c;
            subMask |= std::bit_floor(bitMask ^ subMask);
            prevStart = subMask;
        }
    }
}
 
template<typename Callback>
void iterateSetBits(uint64_t bitMask, Callback&& callback) {
    while (bitMask) {
        auto setBit = bitMask & -bitMask;
        callback(setBit);
        bitMask ^= setBit;
    }
}
 
int parallelCourses2(int n, std::vector<std::vector<int>> const& relations, int k) {
    auto dp = std::vector<std::optional<int>>(1 << n, std::nullopt);
 
    std::vector<int> prerequisites(n, 0);
    for (auto const& relation: relations) {
        prerequisites[relation[1] - 1] |= (1 << (relation[0] - 1));
    }
 
    auto dpFunc = [n = n, k = k, &dp = dp, &prerequisites = std::as_const(prerequisites)](this auto&& dpFunc, int coursesTaken) -> int {
        std::println("called | coursesTaken: {:0{}b}", coursesTaken, n);
        auto& dpVal = dp[coursesTaken];
        if (dpVal) {
            std::println("done   | coursesTaken: {:0{}b} | dpVal: {}", coursesTaken, n, *dpVal);
            return *dpVal;
        }
 
        if (coursesTaken == (1 << n) - 1) {
            dpVal = 0;
            std::println("done   | coursesTaken: {:0{}b} | dpVal: {}", coursesTaken, n, *dpVal);
            return *dpVal;
        }
 
        uint64_t nextCourses = 0;
        iterateSetBits(((uint64_t(1) << n) - 1) ^ coursesTaken, [&](auto const& notTakenCourseMask) {
            auto notTakenCourse = std::countr_zero(notTakenCourseMask);
            if ((coursesTaken & prerequisites[notTakenCourse]) == prerequisites[notTakenCourse]) {
                nextCourses |= (1 << notTakenCourse);
            }
        });
 
        iterateKSetBits(nextCourses, k, [&](auto const& subMask) {
            auto minSemesters = 1 + dpFunc(coursesTaken ^ subMask);
            if (dpVal) {
                dpVal = std::min(*dpVal, minSemesters);
            } else {
                dpVal = minSemesters;
            }
        });
 
        if (dpVal) {
            std::println("done   | coursesTaken: {:0{}b} | dpVal: {}", coursesTaken, n, *dpVal);
            return *dpVal;
        }
 
        std::println("done   | coursesTaken: {:0{}b} | dpVal: {}", coursesTaken, n, *dpVal);
        return *dpVal;
    };
 
    return dpFunc(0);
}
 
int main() {
    {
        auto minSemesters = parallelCourses2(4, {{2, 1}, {3, 1}, {1, 4}}, 2);
        std::println("minSemesters: {}", minSemesters);
    }
    {
        auto minSemesters = parallelCourses2(5, {{2, 1}, {3, 1}, {4, 1}, {1, 5}}, 2);
        std::println("minSemesters: {}", minSemesters);
    }
}