luogu 2157 状压dp

时间:2021-07-18 23:41:34

f[i][j][k]分别代表1-i-1个人全部打完饭时i及其后7个人的状态为j时最后一个打饭的人为i+k的状态下所用的最小时间

当i已经打过饭时 即 j&1 那么 f [i] [j>>1] [k+1] =min(~, f[i] [j] [k]);

如果没有那么枚举其后的打饭的人同时注意要保证忍耐度的条件,所以利用r找i+h+b[i+h]的最小值,也就是i所能选取的最远边界

如果当前循环的h已经超过范围r 那么结束此次更新答案

否则 f[i] [j|(1<<h)] [h] =min(~ , f[i][j][k] + i+k?(t[i+k]^t[i+h]):0)

最终在n+1,处代表前n已经打完饭,j=0代表后面的状态不再选取,k由-8到-1的全部情况

#include<bits/stdc++.h>
#define rep(i,x,y) for(register int i=x;i<=y;i++)
using namespace std;
inline int read(){
int x=,f=;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=-;ch=getchar();}
while(isdigit(ch)){x=(x<<)+(x<<)+(ch^);ch=getchar();}
return x*f;} const int N=;
const int inf=0x3f3f3f3f; int n,t[N],b[N],dp[N][<<][];
#define f(i,j,k) dp[i][j][k+8]
inline void sudo(){ n=read();
rep(i,,n) t[i]=read(),b[i]=read();
memset(dp,inf,sizeof dp);
f(,,-)=;
rep(i,,n)rep(j,,(<<)-)
rep(k,-,)if(f(i,j,k)!=inf)
if(j&) f(i+,j>>,k-)=min(f(i+,j>>,k-),f(i,j,k));
else{
int r=inf;
rep(h,,)if(!((j>>h)&)){//在这个人之前的未打饭的人
if(i+h>r) break;
r=min(r,i+h+b[i+h]);
//距离i的最远距离
//判断i+k就是判断上一次是否是起点
f(i,j|(<<h),h)=min(f(i,j|(<<h),h),f(i,j,k)+(i+k?(t[i+k]^t[i+h]):));
}
}
int ans=inf; rep(k,-,-)
ans=min(ans,f(n+,,k));
printf("%d\n",ans);return;} int main(){
int T=read();while(T--) sudo();return ;}

完结撒花