中文题意
第一行给定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; }