题目描述
有 部电影,每部时长为 共放映 场,且第 部电影第 场的开场时间为 。
每部电影只能看一次,看电影的中途可以换电影,求连续看满时长 最少要看几部电影。
思路
数据范围 ,考虑状压 DP。
设 表示在状态 时,可以连续看多久电影。
对于每个状态 ,暴力枚举接下来我们要看的电影。
因为要求看的电影数量尽可能少,所以每次应该选择最后一个不大于当前时间的电影场次。这个操作可以用 upper_bound
解决。
然后做正常的状压 DP 就可以了,设当前选到了电影 ,只有 存在不大于当前时间的场次,才进行转移。当前状态应当与电影结束时间取最大值,即 。
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
| #include<iostream> #include<algorithm> #include<cmath> #include<cstring> #include<cstdio> #include<cstdlib> using namespace std; template <typename T> inline void read(T&x){ int w=0;x=0; char ch = getchar(); while(ch<'0' || ch>'9'){ if(ch=='-') w = 1; ch = getchar(); } while(ch>='0' && ch<='9'){ x = (x<<1)+(x<<3)+(ch^48); ch = getchar(); } if(w) x=-x; } template <typename T,typename...Args> inline void read(T&t,Args&...args){ read(t);read(args...); } const int N = 25,INF = 0x3f3f3f3f; int n,L; int val[N],num[N],tim[N][1100]; int ans=INF,dp[(1<<20)+10]; int main(){ read(n,L); for(int i=1;i<=n;++i){ read(val[i],num[i]); for(int j=1;j<=num[i];++j){ read(tim[i][j]); } } for(int s=0;s<1<<n;++s){ for(int i=1;i<=n;++i){ if((1<<(i-1))&s) continue; int idx = upper_bound(tim[i]+1,tim[i]+num[i]+1,dp[s])-(tim[i]+1); if(idx>0) dp[s|(1<<(i-1))] = max(dp[s|(1<<(i-1))],tim[i][idx]+val[i]); } if(dp[s]>=L) ans = min(ans,__builtin_popcount(s)); } printf("%d",ans==INF? -1 : ans); return 0; }
|