中文题意

第一行给定n部电影,和观影时间l。
后面n行分别给出第一个参数,这一部电影的播放时长a[i],第二个参数播放次数m,后面m个参数是开始播放的时间点。
问,是否一种观影方式让这个人一直处在看电影的状态,并且不能同时看一部电影2遍,就是刚刚出来又回去看这部电影。

解题思路

看到给出电影数最大20,考虑状态压缩,二进制枚举全部电影情况。
那么这个时候,我们dp数组保存什么呢?保存i状态电影组合下的连续观看,最后的离场时间。
那么我们在每个决策点,去尝试更新dp[i],去不看j号电影(前提j是i电影组合里的一部),在j号电影放映时间中找去掉j号电影的组合下最后离场时间。
如果不看j情况下最后离场时间比j号首映时间长,说明可以不间断,那么我们去更新dp[i]。在每次循环完n个节点之后,判断是否大于l,如果大于l说明可以全程看电影,更新ans,为最少的1的个数。

#include <bits/stdc++.h>
#pragma GCC optimize(2)
#pragma GCC optimize(3)
using namespace std;
#define js ios::sync_with_stdio(false);cin.tie(0); cout.tie(0)
typedef long long ll;

inline int read() {
    int s = 0, w = 1; char ch = getchar();
    while (ch < 48 || ch > 57) { if (ch == '-') w = -1; ch = getchar(); }
    while (ch >= 48 && ch <= 57) s = (s << 1) + (s << 3) + (ch ^ 48), ch = getchar();
    return s * w;
}

const int N = 20;
vector<int> v[N];
int dp[1 << N];
int a[N];

int main() {
    int n = read(), l = read();
    int ans = 30;
    for (int i = 0; i < n; ++i) {
        a[i] = read();
        int m = read();
        while (m--)    v[i].push_back(read()); //太秀了刚刚学到这个写法
    }
    for (int i = 1; i < 1 << n; ++i) {
        for (int j = 0; j < n; ++j) {
            if (i & (1 << j)) {
                int tmp = i ^ (1 << j);
                int it = upper_bound(v[j].begin(), v[j].end(), dp[tmp]) - v[j].begin() - 1; //第一个小于等于dp[tmp]
                if (it >= 0)
                    dp[i] = max(dp[i], v[j][it] + a[j]); 
            }
        }
        if (dp[i] >= l)    ans = min(ans, __builtin_popcount(i));
    }
    if (ans != 30)    printf("%d\n", ans);
    else    puts("-1");
    return 0;
}