题意:
有 N 部电影,每部电影有不同的放映时常,和若干个放映起始时间。
Bessie 可以在一部电影播放过程中的任何时间进入或退出放映厅。每部电影她最多看1次且她不能在看一部电影的过程中,换到另一个正在播放相同电影的放映厅。
Bessie 能不能从0到L分钟连续不断地观看电影?如果能,计算她最少看几部电影。
(1≤L≤100,000,000,1≤N≤20)

题解:
我们发现N很小,只有20,那就可以考虑状压,发现是电影时间是线性的可以通过转移来得到最长时间,那么可以通过状压DP来完成这道题了。

设dp[i]是状态i的时候能看到的最长时间是多少。

枚举每种状态和每种电影,对于如果要通过这部电影j转移,那么需要找到之前还没看这部电影的状态i^(1<<j),然后新的这部电影j的起始时间≤dp[i^(1<<j)],这个可以通过二分去查找,把这个电影起始时间下标记为pos。
那么转移方程为:图片说明

最后通过计算一下每个最长时间超过l的状态有多少个1,更新一下ans就行了。

代码:

#include<cstdio>
#include<iostream>
#include<cmath>
#include<algorithm>
#include<vector>
#include<cstring>
#include<queue>
#define fs first
#define se second
#define pb push_back
#define cppio ios::sync_with_stdio(false);cin.tie(0)
using namespace std;

typedef long long ll;
typedef pair<int,int> pii;
typedef vector<int> VI;

const int maxn=1e3+6;
const ll inf=0x3f3f3f3f;
const ll mod=1e9+7;

int dp[1<<21];

int a[maxn];
int b[maxn][maxn];
int c[maxn];

int main(){
    int n,l;
    scanf("%d%d",&n,&l);
    for(int i=0;i<n;i++){
        scanf("%d",a+i);
        int m;scanf("%d",c+i);
        for(int j=0;j<c[i];j++){
            scanf("%d",&b[i][j]);
        }
    }
    int ans=inf;
    for(int i=0;i<(1<<n);i++){
        for(int j=0;j<n;j++){
            if((1<<j)&i){
                int tmp=i^(1<<j);
                int pos=upper_bound(b[j],b[j]+c[j],dp[tmp])-b[j]-1;
                if(pos>=0){
                    dp[i]=max(dp[i],dp[tmp]+a[j]);
                }
            }
        }
        int tmp=0;
        for(int j=0;j<30;j++){
            if(i&(1<<j)) tmp++;
        }
        if(dp[i]>=l) ans=min(ans,tmp);
    }
    printf("%d",ans);
    return 0;
}