2018.08.29 NOIP模拟 movie(状压dp/随机化贪心)

时间:2022-05-24 06:12:58

【描述】

小石头喜欢看电影,选择有 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


唯一考场上不会正解的题。

写了个70分的随机化贪心(OJ上有90)。

先贴一发:

#include<bits/stdc++.h>
#define N 25
using namespace std;
inline int read(){
    int ans=0;
    char ch=getchar();
    while(!isdigit(ch))ch=getchar();
    while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch^48),ch=getchar();
    return ans;
}
int n,l,d[N],c[N],p[N],sta[N][1005];
inline int check(){
    int pos=0,cnt=0;
    for(int i=1;i<=n;++i){
        int now=p[i];
        int ppos=lower_bound(sta[now]+1,sta[now]+c[now]+1,pos)-sta[now];
        if(sta[now][ppos]>pos)--ppos;
        if(!ppos)return N;
        if(pos>=sta[now][ppos]+d[now])++cnt;
        else pos=sta[now][ppos]+d[now];
        if(pos>l)return i-cnt;
    }
    if(pos<=l)return N;
    return n-cnt;
}
int main(){
//  freopen("movie.in","r",stdin);
//  freopen("movie.out","w",stdout);
    n=read(),l=read();
    for(int i=1;i<=n;++i){
        p[i]=i,d[i]=read(),c[i]=read();
        for(int j=1;j<=c[i];++j)sta[i][j]=read();
    }
    int ans=N;
    for(int i=1;i<=100000;++i)random_shuffle(p+1,p+n+1),ans=min(ans,check());
    cout<<(ans==N?-1:ans);
    return 0;
}

正解是状压dp,f[i]表示观看电影情况为i时能维持的最长时间,转移就从剩下的最接近的转移就行了,查找最接近右端点的值可以用lower_bound,如果想用码量与思维难度来换取时间复杂度的话可以用类似于单调队列的方法优化。

代码(lower_bound查的):

#include<bits/stdc++.h>
#define N 25
using namespace std;
inline int read(){
    int ans=0;
    char ch=getchar();
    while(!isdigit(ch))ch=getchar();
    while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch^48),ch=getchar();
    return ans;
}
int n,l,d[N],c[N],p[N],sta[N][1005],f[3000005],ans=21;
bool vis[3000005];
inline int lowbit(int x){return x&-x;}
inline int calc(int x){int res=0;while(x)x-=lowbit(x),++res;return res;}
inline void modify(int a,int b){vis[a]=true,f[a]=f[a]<b?b:f[a];}
int main(){
    n=read(),l=read();
    for(int i=1;i<=n;++i){
        p[i]=i,d[i]=read(),c[i]=read();
        for(int j=1;j<=c[i];++j)sta[i][j]=read();
    }
    vis[0]=true;
    f[0]=0;
    int up=1<<n;
    for(int i=0;i<up;++i){
        if(!vis[i])continue;
        int tmp=calc(i);
        if(tmp>=ans){i+=lowbit(i)-1;continue;}
        if(f[i]>=l){ans=tmp;continue;}
        for(int j=1;j<=n;++j){
            if((i>>(j-1)&1))continue;
            int pos=lower_bound(sta[j]+1,sta[j]+c[j]+1,f[i])-sta[j];
            if(sta[j][pos]>f[i])--pos;
            if(!pos)continue;
            modify((i|(1<<(j-1))),sta[j][pos]+d[j]);
        }
    }
    cout<<(ans==21?-1:ans);
    return 0;
}