首页 > 其他分享 >1494. 并行课程 II (Hard)

1494. 并行课程 II (Hard)

时间:2023-06-19 11:13:40浏览次数:42  
标签:pre int study Hard relations 学期 II 课程 1494

问题描述

1494. 并行课程 II (Hard)

给你一个整数 n 表示某所大学里课程的数目,编号为 1n ,数组 relations 中, relations[i] = [xᵢ, yᵢ] 表示一个先修课的关系,也就是课程 xᵢ 必须在课程 yᵢ 之前上。同时你还有一个整数 k

在一个学期中,你 最多 可以同时上 k 门课,前提是这些课的先修课在之前的学期里已经上过了。

请你返回上完所有课最少需要多少个学期。题目保证一定存在一种上完所有课的方式。

示例 1:

输入:n = 4, relations = [[2,1],[3,1],[1,4]], k = 2
输出:3
解释:上图展示了题目输入的图。在第一个学期中,我们可以上课程 2 和课程 3 。然后第二个学期上课程 1 ,
第三个学期上课程 4 。

示例 2:

输入:n = 5, relations = [[2,1],[3,1],[4,1],[1,5]], k = 2
输出:4
解释:上图展示了题目输入的图。一个最优方案是:第一学期上课程 2 和 3,第二学期上课程 4 ,第三学期上课
程 1 ,第四学期上课程 5 。

示例 3:

输入:n = 11, relations = [], k = 2
输出:6

提示:

  • 1 <= n <= 15
  • 1 <= k <= n
  • 0 <= relations.length <= n * (n-1) / 2
  • relations[i].length == 2
  • 1 <= xᵢ, yᵢ <= n
  • xᵢ != yᵢ
  • 所有先修关系都是不同的,也就是说 relations[i] != relations[j]
  • 题目输入的图是个有向无环图。

解题思路

本题很容易想到拓扑排序,然后基于拓扑排序进行贪心,但是这个做法是错的!本题实质上是个 NP-Hard 问题,只能暴力求解。

考虑状压 DP,其实很容易想到子问题。即先选择 $1, 2$,再选择 $3, 4, 5$(满足先修的要求)。用二进制数 $to$$study$ 表示当前要学习的课程的集合,用 $pre[j]$ 表示课程 $j$ 的先修课程的集合。那么递归函数即为 $dfs(to$$study, pre)$。

在本次递归中,我们先求出当前可以学习的课程的集合 $tmp$:

for (int i = 0; i < n; ++i) {
    if ((((1 << i) & to_study) != 0) && (pre[i] & to_study) == 0) {
        // 说明 i 在 to_study 中 且 i 的先修课程已经学习过了(或者没有先修课程)
        tmp = (tmp | (1 << i));
        ++cnt;
    }
}

如果 $tmp$ 的课程个数 $cnt <= k$,那么直接学习集合 $tmp$ 的所有课程,否则枚举 $tmp$ 的子集 $i$,如果子集 $i$ 的课程个数 $m$ 满足 $m <= k$,那么就学习该子集,那么下次递归中待学习的课程就可以用 $to$_$study \oplus i$ 表示。

枚举子集的方法参见 位运算与集合

C++ 中统计二进制数的 $1$ 的个数的库函数为 __builtin_popcount(i)

代码

class Solution {
  public:
  	static auto dfs(int to_study, vector<int> &cach, int full, vector<int> &pre, int n, int k) -> int {
        if (to_study == 0) {
            return 0;
        }
  		if (cach[to_study] != -1) {
  			return cach[to_study];
  		}
  		int studied = (to_study ^ full);
  		int cnt = 0; // 统计可以学习的课程数
  		int tmp = 0; // 本次递归要学习的课程
  		for (int i = 0; i < n; ++i) {
  			if ((((1 << i) & to_study) != 0) && (pre[i] & to_study) == 0) {
  				// 说明 i 在 to_study 中 且 i 的先修课程已经学习过了(或者没有先修课程)
  				tmp = (tmp | (1 << i));
  				++cnt;
  			}
  		}
  		if (cnt <= k) {
  			cach[to_study] =  dfs(to_study ^ tmp, cach, full, pre, n, k) + 1;
  			return cach[to_study];
            // return dfs(to_study ^ tmp, cach, full, pre, n, k) + 1;
  		}
  		int res = INT_MAX;
  		for (int i = tmp; i != 0; i = (i - 1) & tmp) {
  			if (__builtin_popcount(i) <= k) {
  				res = min(res, dfs(to_study ^ i, cach, full, pre, n, k) + 1);
  			}
  		}
  		cach[to_study] = res;
  		return cach[to_study];

  	}
    int minNumberOfSemesters(int n, vector<vector<int>> &relations, int k) {
    	// 合集 i 表示要修的课程
    	// 合集 j 是 i 的子集,且 pre(j)(表示 j 的先修课程)与 i 没有交集,并且 size(j) <= k
    	vector<int> pre(n);
    	for (auto &vec : relations) {
    		int x = vec[0] - 1, y = vec[1] - 1; // 下标选择从 0 到 n - 1
    		pre[y] = (pre[y] | (1 << x));	
    	}
    	int full = (1 << n) - 1; // 全集
    	vector<int> cach(1 << n, -1);
    	return dfs(full, cach, full, pre, n, k);

    }
};

标签:pre,int,study,Hard,relations,学期,II,课程,1494
From: https://www.cnblogs.com/zwyyy456/p/17490599.html

相关文章

  • 1494. Parallel Courses II (Hard)
    Description1494.ParallelCoursesII(Hard)Youaregivenanintegern,whichindicatesthattherearencourseslabeledfrom1ton.Youarealsogivenanarrayrelationswhererelations[i]=[prevCoursei,nextCoursei],representingaprerequisiterelat......
  • C++面试八股文:什么是RAII?
    C++面试八股文:什么是RAII?某日二师兄参加XXX科技公司的C++工程师开发岗位第13面:面试官:什么是RAII?二师兄:RAII是ResourceAcquisitionIsInitialization的缩写。翻译成中文是资源获取即初始化。面试官:RAII有什么特点和优势?二师兄:主要的特点是,在对象初始化时获取资源,在对......
  • 552.Student Attendance Record II (Hard)
    Description552.StudentAttendanceRecordII(Hard)Anattendancerecordforastudentcanberepresentedasastringwhereeachcharactersignifieswhetherthestudentwasabsent,late,orpresentonthatday.Therecordonlycontainsthefollowingthre......
  • 1483. Kth Ancestor of a Tree Node (Hard)
    Description1483.KthAncestorofaTreeNode(Hard)Youaregivenatreewithnnodesnumberedfrom0ton-1intheformofaparentarrayparentwhereparent[i]istheparentofithnode.Therootofthetreeisnode0.Findthekthancestorofagive......
  • 30 IIC(八)iic client
    源码1.iicclient创建方法1.1通过设备树直接创建只需要在对应i2c总线下指定设备信息即可示例:需要注意这里i2c1就是I2CBUS01.2通过用户空间直接去生成i2cclient创建i2cclientechonameaddr>/sys/bus/i2c/devices/i2c-n/new_devicei2c-n:i2cadapter删除i2cc......
  • 29 IIC(七)AP3216C
    1.简介AP3216C集成了光强(AmbilentLightSensor,ALS)、距离(ProximitySensor,PS)和红外传感器(InfraredRadiationLED,IR)。该芯片通过IIC接口与主控芯片交互实物图内部结构VDD:3.3VSLC:IICClockGND:LEDA:3.3VLEDC:一般接LDRLDR:LED驱动输出引脚INT:中断输......
  • 已安装过PageOfiice,谷歌浏览器反复提示PageOffice安装
    原因:Chrome开发团队以网络安全为由,强推ssl证书,希望所有部署在公网的网站,全部改用https访问,所以最新的谷歌和edge升级到94版本后对公网上的http请求下的非同域的http请求进行了拦截,于是就出现了目前遇到的反复提示安装pageoffice客户端的问题。解决方案:步骤1:打开谷歌浏览器,在浏览器......
  • 数列分段 III
    太菜了,只会打暴力。首先考虑二分答案。我们发现,有一个结论:如果每段和都小于\(mid\),那么可行划分的段数构成的集合\(S\)一定是一段区间。接下来证明。严谨证明太难写了,下面是半成品,没写完。说人话,就是归纳。假设前\(i-1\)个前缀满足,说明第\(i\)个前缀也满足。我们把......
  • 智能合约HardHat框架环境的搭建
    1.首先创建一个npm项目PSC:\Users\lcds\blockchainprojects>mkdirhardhatcontractPSC:\Users\lcds\blockchainprojects>cd.\hardhatcontract\2.运行 npminit-y  初始化项目PSC:\Users\lcds\blockchainprojects\hardhatcontract>npminit-yWrotetoC:\......
  • 【基础算法】单链表的OJ练习(5) # 环形链表 # 环形链表II # 对环形链表II的解法给出证
    前言本章的OJ练习相对于OJ练习(4)较为简单。不过,本章的OJ最重要的是要我们证明为何可以这么做。这也是==面试==中常出现的。对于OJ练习(4):==->==传送门==<-==,分割链表以一种类似于归并的思想解得,回文链表以一种巧妙复用前面OJ题的思想解得。啰嗦一下:对于本章,最重要的是......