问题描述
小石头喜欢看电影,选择有 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
题解
状压 DP, f[i]表示状态为 i 时从 0 最远连续看到哪。
然后转移时候, 枚举还要看哪部电影,贪心取能看的片场中最靠后的一个。
然后时间复杂度 O(2^N×N×x)
其中 x 是求能看片场中最靠后一个的时间复杂度。
求法 1:
二分。 x=log2C
求法 2:
类似单调队列预处理,然后 x=1
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define INF 0x3f3f3f3f
const int N=25;
int n,l;
int S;
int len[N],movie[N][1010];
int f[1<<N];//f[i]表示状态为i时能看的最长时间
int p[N];
int find(int x,int id)
{
int l=-1,r=p[id]-1,mid;
while(l<r)
{
mid=(l+r+1)>>1;
if(movie[id][mid]<=x)l=mid;
else r=mid-1;
}
return l;
}
int main()
{
//freopen("in.txt","r",stdin);
int i,j,k,ans=INF;
scanf("%d%d",&n,&l);
S=1<<n;
for(i=0;i<n;i++)
{
scanf("%d%d",&len[i],&p[i]);
for(j=0;j<p[i];j++)scanf("%d",&movie[i][j]);
}
memset(f,-1,sizeof(f));
f[0]=0;
for(i=0;i<S;i++)
{
if(f[i]==-1)continue;
if(f[i]>=l)
{
for(j=0,k=i;k;k-=k&(-k))j++;
ans=min(ans,j);
continue;
}
for(j=0;j<n;j++)
{
if(i&(1<<j))continue;
k=find(f[i],j);
if(k==-1)continue;
f[i|(1<<j)]=max(f[i|1<<j],movie[j][k]+len[j]);
}
}
if(ans==INF)puts("-1");
else printf("%d\n",ans);
return 0;
}
比赛时看数据规模感觉是状压DP来着……但是写不来