一.闲话
英文题真难受,盯google盯了好久qwq,输入部分还是看样例解释看出来的qwq,英语菜鸡没人权qwq
二.题解
这道题,题意是:
有n场电影,每场电影都有若干场。
给你每个电影的播放时间以及每场的开始时间,问你,要连续不断的看电影直到看到l为止,最少需要看几部电影(每部电影最多看一场)
明显的dp/贪心题目,仔细分析后,发现并不能贪心,所以考虑dp
注意到,l很大,所以直接dp的话是不行的,但是,n很小(n<=20),于是我们从这里入手,n<=20的话,那么一般考虑搜索或者状压dp(复杂度正好合适),首先,搜索的话,我们只能枚举看哪些电影,至于看哪场,就又需要考虑了,所以暂不考虑搜索。
然后,就是状压dp了。
我们明显可以设dp[i]表示现在状态为i,我们最多可以看到多久。
如果我们要从状态x转移到其他状态,肯定就是枚举当前看哪部电影。
那么,我们方程怎么办?
我们对每一部我们枚举的电影,我们找到开始时间小于等于dp[x]的最大的那个(也即是我们当前能看的这部电影中,最晚结束的那部),因为这样才能把转移过去的dp变得最大,而,直接枚举哪场肯定布星,我们发现,题目中说了,开始时间是递增的(不是递增我们排个序就好2333),于是,找到"最晚的符合条件的开始时间"是可以通过二分来实现的。
最后,我们算完之后,枚举每个状态,看哪些状态的dp值大于等于l,再在这些状态中找1的个数最少的那个即可~
代码:
#include<bits/stdc++.h> using namespace std; const int N=(1<<20),M=21; int dp[N]; int D[M],C[M],T[M][1001]; int main(){ int n,l; scanf("%d%d",&n,&l); for(int i=1;i<=n;++i){ scanf("%d%d",&D[i],&C[i]); for(int j=1;j<=C[i];++j){ scanf("%d",&T[i][j]); } } int tot=(1<<n)-1; for(int i=0;i<tot;++i){ for(int j=1;j<=n;++j){ if((i>>(j-1))&1)continue; int v=(i|(1<<(j-1))); dp[v]=max(dp[v],dp[i]); int l=1,r=C[j],res=0; while(l<=r){ int mid=(l+r)>>1; if(T[j][mid]>dp[i]){ r=mid-1; }else if(T[j][mid]+D[j]<=dp[i]){ l=mid+1; }else{ res=mid,l=mid+1; } } if(res){ dp[v]=max(dp[v],T[j][res]+D[j]); } } } int ans=2e9; for(int i=0;i<=tot;++i){ if(dp[i]>=l){ int res=0; for(int j=1;j<=n;++j){ res+=((i>>(j-1))&1); } ans=min(ans,res); } } if(ans==2e9){//无解 puts("-1"); return 0; } printf("%d",ans); return 0; }