题目大意
奶牛贝西想连续看L (1 <= L <= 100,000,000)分钟的电影,有 N (1 <= N <= 20)部电影可供选择,每部电影会在一天的不同时段放映一次或多次。
贝西可以在一部电影播放过程中的任何时间进入或退出放映厅。但她不愿意重复看到一部电影,所以每部电影她最多看到一次。她也不能在看一部电影的过程中,换到另一个正在播放相同电影的放映厅。
请帮贝西计算她能够做到从0到L分钟连续不断地观看电影,如果能,请计算她最少看几部电影就行了。
题目分析
观察n的数据范围,并且“所以每部电影她最多看到一次”,不难看出这题应该是个状压DP。
将01串 i 作为DP状态,表示观看 i 中选中的电影所能达到的最长时间。
考虑如何转移。我们枚举一个不再当前01串 i 中的电影 j ,因为可以随时离场,所以我们要找到一个最晚的时间 Tj ,
使得 Tj <= f[i] ,则 f[i]=max( f[i] ,Tj + Lenj )。
若当前枚举到的状态 i 已经可以使 f[i]>=L 了,则说明此时已经可以更新答案,
调用__builtin_popcount()函数快速得出 i 中含 '1' 的个数更新答案即可。
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int MAXN=25; 4 const int MAXT=1e3+10; 5 6 int n,l,ans; 7 int len[MAXN],c[MAXN],s[MAXN][MAXT]; 8 int f[1<<22]; 9 10 inline int Find(int val,int *a,int siz){ 11 int l=-1,r=siz-1,res=-1; 12 while(l<=r){ 13 int mid=(l+r)>>1; 14 if(a[mid]<=val){ 15 l=mid+1; 16 res=mid; 17 } 18 else 19 r=mid-1; 20 } 21 return res; 22 } 23 int main(){ 24 scanf("%d%d",&n,&l); 25 for(int i=0;i<n;++i){ 26 scanf("%d%d",&len[i],&c[i]); 27 for(int j=0;j<c[i];++j) 28 scanf("%d",&s[i][j]); 29 } 30 31 for(int i=1;i<(1<<n);++i) f[i]=-1; 32 ans=-1; 33 for(int now=0;now<(1<<n);++now){ 34 if(f[now]==-1) continue; 35 if(f[now]>=l) 36 if(ans==-1||__builtin_popcount(now)<ans) 37 ans=__builtin_popcount(now); 38 39 for(int i=0;i<n;++i){ 40 if(now&(1<<i)) continue; 41 int nw=now|(1<<i); 42 int pos=Find(f[now],s[i],c[i]); 43 if(pos==-1) continue; 44 f[nw]=max(f[nw],s[i][pos]+len[i]); 45 } 46 } 47 printf("%d ",ans); 48 return 0; 49 }