[bzoj3886] [USACO15JAN]电影移动Moovie Mooving

题目链接

状压\(dp\)

注意到\(n\leq 20\)且每个只能用一次,所以很显然可以压缩每部电影看过没,记\(f[sta]\)为状态为\(sta\)时最多可以看多久。

转移时先枚举状态,然后枚举没看过的电影,二分出一个小于\(f[sta]\)的时间点进行转移。

答案就是所有大于\(L\)的状态的\(1\)的个数的最小值。

#pragma GCC optimize(3)
#include<bits/stdc++.h>
using namespace std;
#define int long long
void read(int &x){
    x=0;int f=1;char ch=getchar();
    for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=-f;
    for(;isdigit(ch);ch=getchar()) x=(x<<1)+(x<<3)+ch-'0';x*=f;
}
#define write(x) printf("%lld\n",x)
#define maxn 1002
int f[(1<<20)+5],t[2001],s[21][1005],n,ed,cnt[(1<<20)+5],ans=1e9;
int calc(int x){
    int res=0;
    while(x) res+=(x&1),x>>=1;
    return res;
}
signed main(){
    //freopen("testdata(1).in","r",stdin);
    read(n),read(ed);
    for(int i=1;i<=n;i++) {
        read(t[i]),read(s[i][0]);
        for(int j=1;j<=s[i][0];j++) read(s[i][j]);
    }
    memset(f,-1,sizeof f);f[0]=0;
    for(int i=0;i<(1<<n);i++) cnt[i]=calc(i);
    for(int i=0;i<(1<<n);i++){
        for(int j=1;j<=n;j++)
            if(!(i&(1<<(j-1)))){
                int p=upper_bound(s[j]+1,s[j]+s[j][0]+1,f[i])-s[j];
                if(p>1) f[i|(1<<(j-1))]=max(f[i|(1<<(j-1))],s[j][p-1]+t[j]);
                else f[i|(1<<(j-1))]=max(f[i|(1<<(j-1))],f[i]);
            }
        if(f[i]>=ed) ans=min(ans,cnt[i]);
    }
    if(ans==1e9) puts("-1");
    else write(ans);
    return 0;
}

posted @ 2018-10-22 19:49  Hyscere  阅读(99)  评论(0编辑  收藏  举报