P3118 [USACO15JAN]Moovie Mooving G 狀壓DP + 二分
阿新 • • 發佈:2020-09-12
P3118 [USACO15JAN]Moovie Mooving G
題目連結
狀壓DP + 二分。
由於電影的數目很小,並且每個電影只能看一次,我們可以用二進位制狀壓,第\(i - 1\)位為0/1代表第\(i\)個電影沒看/看了。
\(f[s]\)表示在狀態\(s\)下,最多可以從0連續看到多長時間。
然後列舉狀態\(s\)的每一個1,去掉這個1會得到一個新的狀態\(s1\),設當前1的位置為\(j\),我們要找一個小於等於\(f[s1]\)的最大的一個開始時間,然後轉移:\(f[i] = max(f[i], a[j][tmp] + len[j]);\)(\(tmp\)表示找到的最大的開始時間的編號,\(len\)
#include <bits/stdc++.h> using namespace std; inline long long read() { long long s = 0, f = 1; char ch; while(!isdigit(ch = getchar())) (ch == '-') && (f = -f); for(s = ch ^ 48;isdigit(ch = getchar()); s = (s << 1) + (s << 3) + (ch ^ 48)); return s * f; } const int N = 25; int n, l, ans; int f[1 << 20], num[N], sum[N], len[N], a[N][1005]; int find(int x, int p) { int l = 1, r = num[p]; int res = 0; while(l <= r) { int mid = (l + r) >> 1; if(a[p][mid] <= x) res = mid, l = mid + 1; else r = mid - 1; } return res; } int main() { n = read(); l = read(); for(int i = 1;i <= n; i++) { len[i] = read(); num[i] = read(); for(int j = 1;j <= num[i]; j++) a[i][j] = read(); } ans = 1e9; for(int i = 1;i <= (1 << n) - 1; i++) { for(int j = 1;j <= n; j++) { if(i & (1 << (j - 1))) { int tmp = find(f[i ^ (1 << (j - 1))], j); if(tmp != 0) f[i] = max(f[i], a[j][tmp] + len[j]); } } } for(int i = 1;i <= (1 << n) - 1; i++) { sum[i] = sum[i ^ (i & (-i))] + 1; if(f[i] >= l) ans = min(ans, sum[i]); } printf("%d", ans == 1e9 ? -1 : ans); fclose(stdin); fclose(stdout); return 0; }