P3118 [USACO15JAN]Moovie Mooving G

比较有意思且经典的状压。

题目简介

题目来源:

评测链接:https://www.luogu.com.cn/problem/P3118

形式化题意:给定 组线段,第 组包含 条线段,长度为 ,起始点分别为 ,能否在每一组线段至多被使用一次的情况下填满 ,可重,求最少使用多少组。

考虑到 ,就可以状压一波了。用 表示使用了集合 中的电影能够观看的最长时间(那么当 的时候 就可以被统计进答案),然后考虑内部转移,我们首先是要保证可重不漏,而每一个电影都要对答案产生最大的贡献,我们考虑以下的步骤。

当前集合为 ,我们需要观看的是 ,那么要让 产生最大的贡献,我们就需要从电影 中找到一个既能保证不漏,又可以推进最大时间的,也就是最后一个满足 ,用 std::upper_bound 可以解决。

AC Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
// ----- Eternally question-----
// Problem: P3118 [USACO15JAN]Moovie Mooving G
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P3118
// Memory Limit: 125 MB
// Time Limit: 1000 ms
// Written by: Eternity
// Time: 2022-12-28 15:25:30
// ----- Endless solution-------

#include<bits/stdc++.h>
#define re register
typedef long long ll;
template<class T>
inline void read(T &x)
{
x=0;
char ch=getchar(),t=0;
while(ch<'0'||ch>'9') t|=ch=='-',ch=getchar();
while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
if(t) x=-x;
}
template<class T,class ...T1>
inline void read(T &x,T1 &...x1){ read(x),read(x1...); }
template<class T>
inline void write(T x)
{
if(x<0) putchar('-'),x=-x;
if(x>9) write(x/10);
putchar(x%10+'0');
}
template<>
inline void write(bool x){ std::cout<<x; }
template<>
inline void write(char c){ putchar(c); }
template<>
inline void write(char *s){ while(*s!='\0') putchar(*s++); }
template<>
inline void write(const char *s){ while(*s!='\0') putchar(*s++); }
template<class T,class ...T1>
inline void write(T x,T1 ...x1){ write(x),write(x1...); }
template<class T>
inline bool checkMax(T &x,T y){ return x<y?x=y,1:0; }
template<class T>
inline bool checkMin(T &x,T y){ return x>y?x=y,1:0; }
const int MAXN=22,MAXC=1e3+10,MAXS=(1<<20)+10;
const int INF=0x3f3f3f3f;
int L,N,Te[MAXN],Lh[MAXN][MAXC],Dp[MAXS],Popcnt[MAXS];
int main()
{
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
read(N,L);
for(int i=1;i<=N;++i)
{
read(Te[i],Lh[i][0]);
for(int j=1;j<=Lh[i][0];++j) read(Lh[i][j]);
}
std::memset(Dp,0xcf,sizeof(Dp)),Dp[0]=0;
for(int s=1;s<(1<<N);++s) Popcnt[s]=Popcnt[s>>1]+(s&1);
for(int s=0;s<(1<<N);++s)
{
if(Dp[s]<0) continue;
for(int j=1;j<=N;++j)
if(!((s>>(j-1))&1))
{
int lst=std::upper_bound(Lh[j]+1,Lh[j]+Lh[j][0]+1,Dp[s])-Lh[j]-1;
if(lst&&Lh[j][lst]+Te[j]>Dp[s]) checkMax(Dp[s|(1<<(j-1))],Lh[j][lst]+Te[j]);
}
}
int ans=INF;
for(int s=0;s<(1<<N);++s) if(Dp[s]>=L) checkMin(ans,Popcnt[s]);
write(ans==INF?-1:ans);
return 0;
}
/*

*/