P3118 [USACO15JAN] Moovie Mooving G
P3118 [USACO15JAN] Moovie Mooving G
题目描述
有 \(N\) 部电影,每部时长为 \(val_i\) 共放映 \(num_i\) 场,且第 \(i\) 部电影第 \(j\) 场的开场时间为 \(tim_{i,j}\)。
每部电影只能看一次,看电影的中途可以换电影,求连续看满时长 \(L\) 最少要看几部电影。
思路
数据范围 \(1\le N\le 20\),考虑状压 DP。
设 \(dp_s\) 表示在状态 \(s\) 时,可以连续看多久电影。
对于每个状态 \(s\),暴力枚举接下来我们要看的电影。
因为要求看的电影数量尽可能少,所以每次应该选择最后一个不大于当前时间的电影场次。这个操作可以用 upper_bound 解决。
然后做正常的状压 DP 就可以了,设当前选到了电影 \(i\),只有 \(i\) 存在不大于当前时间的场次,才进行转移。当前状态应当与电影结束时间取最大值,即 \(\max(dp_{s|(1<<(i-1))} , tim_{i,idx}+ val_i)\)。
代码
#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<cstdio>
#include<cstdlib>
using namespace std;
template <typename T>
inline void read(T&x){//快读
int w=0;x=0;
char ch = getchar();
while(ch<'0' || ch>'9'){
if(ch=='-') w = 1;
ch = getchar();
}
while(ch>='0' && ch<='9'){
x = (x<<1)+(x<<3)+(ch^48);
ch = getchar();
}
if(w) x=-x;
}
template <typename T,typename...Args>
inline void read(T&t,Args&...args){
read(t);read(args...);
}
const int N = 25,INF = 0x3f3f3f3f;
int n,L;
int val[N],num[N],tim[N][1100];
int ans=INF,dp[(1<<20)+10];
int main(){
read(n,L);
for(int i=1;i<=n;++i){
read(val[i],num[i]);//电影时长、场次数量
for(int j=1;j<=num[i];++j){
read(tim[i][j]);//每场的开始时间
}
}
for(int s=0;s<1<<n;++s){
for(int i=1;i<=n;++i){
if((1<<(i-1))&s) continue;//电影已经选过
int idx = upper_bound(tim[i]+1,tim[i]+num[i]+1,dp[s])-(tim[i]+1);
if(idx>0) dp[s|(1<<(i-1))] = max(dp[s|(1<<(i-1))],tim[i][idx]+val[i]); //存在场次
}
if(dp[s]>=L) ans = min(ans,__builtin_popcount(s));//popcount非常快
}
printf("%d",ans==INF? -1 : ans);
return 0;
}

浙公网安备 33010602011771号