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;
}
posted @ 2025-03-14 21:40  Tmbcan  阅读(15)  评论(0)    收藏  举报