状压+卡常
状态和转移应该算是挺简单的,在转移过程中二分枚举一下就可以了。
复杂度:(2^n*n *log2( c ))/2(除以2,是因为当新枚举的那个i不在状态p中才进行转移)。这个复杂度好像有点慌啊,刚好一亿多出几十万。再加一个max,和几个位运算,加减运算,感觉会超时,事实证明,确实超时了。
可能是我不太会卡常的缘故,这里给一个卡过去的别的人博客。洛谷AC
下面来考虑下,不卡常的做法。
很多dp中,n^3 都可以优化为 n^2 + n^2 来做,就是预处理一下的事。这里,也可以优化为2^n*n + (…)的复杂度。 括号里的省略是因为,要预处理的东西挺多的,但是都挺小,所以就暂时不写了,后面会讲到。(简而言之,就是把原来dp中的二分拿出来预处理好)这样的话,dp里就不用再次二分答案,直接转移即可。
下面给出优化思路过程(对于里面的变量名,和代码中相同,就不再介绍,自行根据代码理解)
未优化前的dp[p]表示的是,看过的电影的状态是p,最长可以消耗的时间是dp[p]。
对于未知的dp[p],要拿出来预处理,怎么存,不可做啊。后来发现,每一个dp[p]的值,都是某一个a[i][j]+t[i],那么,我们就把这所有的a[i][j]+t[i]拎出来,放进b数组。dp中,只会出现这几个值。
现在,dp[p]的意义就可以改为:看过的电影的状态是p,最长可以消耗的时间,在b数组中的下标是dp[p]。(这里,通过离散化,把原来很大的dp[p]变成只有1000 * 20了。1000 * 20,不就可以开个数组存下来了嘛,也就是说,预处理可以存下来了)(这里的1000 * 20是指,所有可能出现的a[i][j]+t[i]的个数)
改一下状态的定义,然后离散化一波,就成功把dp过程中计算的二分提到了外面完成,不需要卡常,stl随便用,稳稳跑过。
#include <bits/stdc++.h>
#define lowbit(x) x&(-x)
using namespace std;
const int N=21,M=1e3+5;
int n,len,cnt,max_statue,pos,ans;
int t[N],a[N][M],b[N*M],to[N*M][N],nxt[N][M],f[1<<N];
inline int query(int x)
{
int res=0;
while (x)
{
x-=lowbit(x);
res++;
}
return res;
}
int main(){
scanf("%d%d",&n,&len);
for (register int i=1; i<=n; ++i)
{
scanf("%d%d",&t[i],&a[i][0]);
for (register int j=1; j<=a[i][0]; ++j)
{
scanf("%d",&a[i][j]);
cnt++; b[cnt]=a[i][j]+t[i];
}
}
cnt++; b[cnt]=0;
sort(b+1,b+cnt+1);
cnt=unique(b+1,b+cnt+1)-b-1;
for (register int i=1; i<=cnt; ++i)
for (register int j=1; j<=n; ++j) to[i][j]=upper_bound(a[j]+1,a[j]+a[j][0]+1,b[i])-a[j]-1;
for (register int i=1; i<=n; ++i)
for (register int j=1; j<=a[i][0]; ++j) nxt[i][j]=lower_bound(b+1,b+cnt+1,a[i][j]+t[i])-b;
f[0]=1;
max_statue=(1<<n)-1;
for (register int p=0; p<max_statue; ++p)
for (register int i=1; i<=n; ++i)
if (!((1<<(i-1))&p))
{
pos=to[f[p]][i];
if (!pos) continue;
f[p|(1<<(i-1))]=max(f[p|(1<<(i-1))],max(f[p],nxt[i][pos]));
}
ans=2e9;
for (register int p=0; p<max_statue; ++p) if (b[f[p]]>=len) ans=min(ans,query(p));
if (ans==2e9) puts("-1"); else printf("%d\n",ans);
return 0;
}