【题目】
题目描述:
小石头喜欢看电影,选择有 N 部电影可供选择,每一部电影会在一天的不同时段播放。他希望连续看 L 分钟的电影。因为电影院是他家开的,所以他可以在一部电影播放过程中任何时间进入或退出,当然他不希望重复看一部电影,所以每部电影他最多看一次,也不能在看一部电影的时候,换到另一个正在播放一样电影的放映厅。
请你帮助小石头让他从 0 到 L 连续不断的看电影,如果可以的话,计算出最少看几部电影。
输入格式:
第一行是 2 个整数 N , L,表示电影的数量,和小石头希望看的连续时间 接下来是 N 行,每行第一个整数 D(1 ≤ D ≤ L)表示电影播放一次的播放时间,第二个整数是 C 表示这部电影有 C 次播放,接下来是 C 个整数表示 C 次播放的开始时间 Ti(0 ≤ Ti ≤ L), Ti 是按升序给出
输出格式:
一个整数,表示小石头最少看的电影数量,如果不能完成输出 -1
样例数据:
输入
4 100 50 3 15 30 55 40 2 0 65 30 2 20 90 20 1 0
输出
3
备注:
【样例说明】
开始他选择最后一步电影从 0 时间开始。
到了 20 分钟,他选择第一部电影的第一次播放,看到 65 分钟
最后他选择第二部电影的第二次播放,从 65 分钟到 100 分钟
【数据规模】
30% 数据 N ≤ 10
100% 数据 N ≤ 20 , 1 ≤ L ≤ 100,000,000 ,C ≤ 1000
【分析】
状压DP,用 f[ i ] 表示从 0 开始最远能连续看到哪
转移的时候,枚举没有看过的电影,贪心选取能看的片场中最靠后的那一个
我们可以用二分来找要选的那一部电影
时间复杂度O()
【代码】
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 25
#define C 1005
#define inf 1ll<<31ll-1
#define lowbit(x) x&(-x)
using namespace std;
int d[N],c[N],t[N][C],f[1<<N];
int find(int x,int id)
{
int l,r,mid;
l=-1,r=c[id];
while(l<r)
{
mid=(l+r+1)>>1;
if(t[id][mid]<=x) l=mid;
else r=mid-1;
}
return l;
}
int main()
{
// freopen("movie.in","r",stdin);
// freopen("movie.out","w",stdout);
int n,l,i,j;
scanf("%d%d",&n,&l);
int status=1<<n;
for(i=1;i<=n;++i)
{
scanf("%d%d",&d[i],&c[i]);
for(j=1;j<=c[i];++j)
scanf("%d",&t[i][j]);
}
memset(f,-1,sizeof(f));
f[0]=0;
int ans=inf;
for(i=0;i<status;++i)
{
if(f[i]==-1)
continue;
if(f[i]>=l)
{
int num=0;
for(j=i;j;j-=lowbit(j))
num++;
ans=min(ans,num);
continue;
}
for(j=1;j<=n;++j)
{
if(i&(1<<j-1))
continue;
int pos=find(f[i],j);
if(pos==-1) continue;
f[i|(1<<j-1)]=max(f[i|(1<<j-1)],t[j][pos]+d[j]);
}
}
if(ans==inf) printf("-1");
else printf("%d",ans);
// fclose(stdin);
// fclose(stdout);
return 0;
}