题目:
n部电影,有时长和若干放映起始时间。Bessie可以在某部电影的放映阶段去看这部电影或中途离开。他不会看同一部电影2次。问Bessie最少需要看几部电影使得他在0~L时间段内一直看电影。(n最大20)
做法:
状压DP。 表示在状态下Bessie能连续看电影到的最远时间。是二进制状态。第i位为1表示第i部电影已经看过,0表示没看过。转移的时候就找一个中的一个0将它置1。表示下一部要看的电影就是它。然后在这部电影的放映时间vector中二分第一个比当前时间小的放映时间。用+电影时长更新就好了。
转移:
统计最少的电影数量:
代码:
#include <bits/stdc++.h> #define IOS ios::sync_with_stdio(false), cin.tie(0) #define debug(a) cout << #a ": " << a << endl using namespace std; typedef long long ll; const int N = 21; const int inf = 0x3f3f3f3f; int movie_time[N], dp[1<<N]; vector<int> movie_begin[N]; int get(int id, int x){ int p = upper_bound(movie_begin[id].begin(), movie_begin[id].end(), x) - movie_begin[id].begin(); if (p == 0) return 0; return movie_begin[id][p-1]+movie_time[id]; } int cntbit(int sta){ int cnt = 0; for (int i = sta; i >= 1; i -= i&(-i)) cnt++; return cnt; } int main(void){ IOS; int n, L; cin >> n >> L; for (int i = 1; i <= n; ++i){ cin >> movie_time[i]; int m; cin >> m; for (int j = 1; j <= m; ++j){ int x; cin >> x; movie_begin[i].push_back(x); } } for (int i = 1; i <= n; ++i){ if (movie_begin[i][0] == 0) dp[1<<(i-1)] = movie_time[i]; } int ans = inf; for (int i = 1; i < (1<<n); ++i){ if (dp[i] >= L) ans = min(ans, cntbit(i)); for (int j = 0; j < n; ++j){ if (((i>>j)&1) == 0){ dp[i|(1<<j)] = max(dp[i|(1<<j)], get(j+1, dp[i])); } } } if (ans == inf) ans = -1; cout << ans << endl; return 0; }