题意:奶牛要在电影院里呆L分钟,这段时间他要看电影度过。电影一共N部,每部都播放于若干段可能重叠的区间,奶牛决不会看同一部电影两次。现在问他要看最少几部电影才能度过这段时间? 注:必须看电影才能在电影院里呆着,同时一场电影可以在其播放区间内任意时间入场出场。
题解:设dp[s]代表已看的电影集合为s所能待到的最长时间,转移的时候枚举一个没有看过的电影,二分找到最近的开始时间,直接转移即可,详情见代码。
//n<=20 枚举其状态 dp[s] s状态下最大的滞留时间
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e3+5;
void io() { ios::sync_with_stdio(0);cin.tie(0);cout.tie(0); }
int a[25][maxn],dp[(1<<21)+5];
int L,n,t[25],cnt[25];
//二分查找
int find(int x,int i) {
int l=0,r=cnt[i],res=0;
while(l<=r) {
int mid=(l+r)/2;
if(x<a[i][mid]) r=mid-1;
else l=mid+1,res=mid;
}
return res;
}
int main() {
int ans=25;
scanf("%d%d",&n,&L);
for(int i=1;i<=n;i++) {
scanf("%d%d",t+i,cnt+i);
for(int j=1;j<=cnt[i];j++) scanf("%d",&a[i][j]);
}
memset(dp,-1,sizeof(dp));
dp[0]=0; //初始化
for(int s=0;s<1<<n;s++) {
if(dp[s]==-1) continue;
if(dp[s]>=L){
ans=min(ans,__builtin_popcount(s)); //__builtin_popcount(s)统计s二进制中1的个数
}
//枚举一个未观看的电影
for(int i=1;i<=n;i++) {
if(!(s&(1<<(i-1)))) {
int pos=find(dp[s],i);
if(pos==0) continue;
dp[s|(1<<(i-1))]=max(dp[s|(1<<(i-1))],a[i][pos]+t[i]);
}
}
}
if(ans==25) cout<<-1<<endl;
else cout<<ans<<endl;
return 0;
}