LeetCode1494 并行课程II——状态压缩DP

前言

当输入的个数比较少,而需要考虑其各种组合的状态时,可以使用状态压缩DP,用某一位的0或1来表示某一个元素的状态,用一个int值来表示整体的状态,对这个int值进行迭代。

题目

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

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

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

分析

我们用一个int值state来表示当前已修课程的状态,对于state的第i位,如果为1表示第i门课程已修,为0表示第i门课程未修。dp[state]表示达到状态state所需的最少学期数。

我们从0开始迭代state。为什么可以从0开始迭代呢?这是因为我们注意到随着state的值增大,state中包含1的位逐渐向左拓展,因此对于一个state来说,其前置的状态为1的位,该state的对应位置也一定为1,换句话说,state的前置状态在int值上一定是小于state的,因此state可以从小到大迭代。

在state状态下,我们找到所有当前能修的课程,如何判断某门课程是否能修呢?我们使用一个掩码来表示某门课程的依赖课程,依赖的课程位置为1,这样我们当前的state按位或某门课程的掩码,如果state不变,则表明这门课程的依赖课程都满足了,我们维护一个当前可修课程的int值,将该门课程的对应位置1。

找到了我们在当前学习可以修的所有课程后,我们选择不多于k门课程来学习。我们该如何选择课程呢?假设有m门课程我们可以选择,我们是否需要枚举从m门中选择不多于k门课的所有可能呢?
答案是不需要。我们考虑这样的情况,假设在状态state下可以选择m门课,但我们最多选择k门课,如果我们只从m门课中序号较高的那k门课中选择,我们是否会漏掉某些状态呢?不会的,记我们选择了序号较高的k门课后,状态变为了state’,没有选到的课程在state’状态下依然是可选的,这样虽然我们之前没有选,但之后还是考虑到了这种情况,实际上整个解集是完备的。因此不会漏掉可能性。

代码

class Solution {
    public int minNumberOfSemesters(int n, int[][] dependencies, int k) {
        int N = 1<<n;
        int[] mask = new int[n];
        int[] dp = new int[N];
        int[] cnt = new int[N];
        
        //init
        Arrays.fill(dp, n+1);
        dp[0] = 0;
        for(int[] i : dependencies) {
            mask[i[1]-1] |= 1<<i[0]-1;
        }
        cnt[0] = 0;
        for(int i = 1; i < N; i++) {
            cnt[i] = cnt[i>>1]+(i&1);
        }
        
        //dp
        for(int i = 0; i < N; i++) if(dp[i] < n) {
            int cur = 0;
            for(int j = 0; j < n; j++) if((i>>j&1)==0 && (mask[j]|i)==i){
                cur |= 1<<j;
            }
            for(int j = cur; j != 0; j = j-1&cur) if(cnt[j] <= k) {
                dp[i|j] = Math.min(dp[i|j], dp[i]+1);
            }
        }
        
        return dp[N-1];
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值