1 条题解
-
0
自动搬运
来自洛谷,原作者为

WZKQWQ
还活着搬运于
2025-08-24 21:45:28,当前版本为作者最后更新于2020-08-04 17:40:24,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
这篇题解受大佬NewErAd的启发而出
其实我就是让大家更好的理解它的思路看了记录是第2快的(因为我懒得用快读,第一用了快读快输)
和第一只差0.05s
珂爱的分割线
介绍思想:
dp[i]表示集合i能最多看几分钟电影,
转移呼之欲出
选一个不在集合的点j把它加入
当j最小的电影开始时间小于dp[i]时
dp[i |(1 << j)] = max(dp[i |(1 << j)],a[j][#] + d[j]);
(#是最好的电影开始时间,后面会求)
不然考虑继承状态:dp[i |(1 << j)] = dp[i];
当找到一个dp[i] > L时:ans = min(ans,cnt1(i));
int cnt1(int x) { int num = 0; while (x) { if (x & 1) num++; x = x >> 1; } return num; }这个数1函数不用我说了吧。
现在我们来看前面的#怎么求
upper_bound以a[j] + 1, a[j] + a[j][0] + 1, f[i]) - a[j];
这个函数是求以a[j] + 1开头,以a[j] + a[j][0] + 1结尾
(这里我用a[j][0]保存一部电影有几场)
的数组里不大于f[i]的第一个数的下标。
当它小于1时就不存在这个数。
伪代码来一波:
for (int i = 0; i < DP; i++) { if(f[i] == -1) continue; for (int j = 0; j < n; j++) { if (i & (1 << j)) continue; int p = upper_bound(a[j] + 1, a[j] + a[j][0] + 1, f[i]) - a[j]; if (p > 1) f[i | (1 << j)] = max(f[i | (1 << j)], a[j][p - 1] + d[j]); else f[i | (1 << j)] = f[i]; } if (f[i] >= m) ans = min(ans, cnt1(i)); }其他代码呼之欲出,都是细节:
看在写题解那么辛苦的份上点个赞再走吧你们在等的:
#include <bits/stdc++.h> using namespace std; int n, m, d[25], a[25][1005], f[1 << 20]; int cnt1(int x) { int num = 0; while (x) { if (x & 1) num++; x = x >> 1; } return num; } int main() { scanf("%d%d", &n, &m); for (int i = 0; i < n; i++) { scanf("%d%d", &d[i], &a[i][0]); for (int j = 1; j <= a[i][0]; j++) scanf("%d", &a[i][j]); } int DP = 1 << n, ans = 1e9; memset(f,-1,sizeof(f)); f[0] = 0; for (int i = 0; i < DP; i++) { if(f[i] == -1) continue; for (int j = 0; j < n; j++) { if (i & (1 << j)) continue; int p = upper_bound(a[j] + 1, a[j] + a[j][0] + 1, f[i]) - a[j]; if (p > 1) f[i | (1 << j)] = max(f[i | (1 << j)], a[j][p - 1] + d[j]); else f[i | (1 << j)] = f[i]; } if (f[i] >= m) ans = min(ans, cnt1(i)); } if (ans == 1e9) printf("-1"); else printf("%d", ans); return 0; }
- 1
信息
- ID
- 2182
- 时间
- 1000ms
- 内存
- 125MiB
- 难度
- 5
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者