USACO 2015 January Contest Gold T2: Moovie Mooving

题目大意

奶牛贝西想连续看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 }
原文地址:https://www.cnblogs.com/LI-dox/p/11213648.html