bzoj1226/luogu2157 学校食堂 (状压dp)
我们先约定:(左) 窗口_人人人人人 (右)
可以发现,我们只需要知道最靠左的还没打饭的人 以及它身后7个人的状态 以及上一个打饭的人是谁
因为他左面的就都打过了 右面7个人以后肯定还没打
可以设f[i][j][k]表示这是第i个人,身后7个人的状态是j,上一个打饭的是k
但其实上一个打饭的离他最远也就是8,所以可以只记i和k的差值,为了避免出负数再加个10之类的
考虑怎么转移,有两种情况
1.i身后的某个人打饭:找到j中是0的一位x,同时保证x左面的没打饭的人+这个人的容忍度<=x,也就是保证这个转移合法
2.i自己打饭:找到i右面第一个是0的位置,转移到这个位置
转移的时候更新一下k就好了
1 #include<bits/stdc++.h> 2 #define CLR(a,x) memset(a,x,sizeof(a)) 3 using namespace std; 4 typedef long long ll; 5 typedef pair<int,int> pa; 6 const int maxn=1050,maxs=260,maxb=20; 7 8 inline ll rd(){ 9 ll x=0;char c=getchar();int neg=1; 10 while(c<'0'||c>'9'){if(c=='-') neg=-1;c=getchar();} 11 while(c>='0'&&c<='9') x=x*10+c-'0',c=getchar(); 12 return x*neg; 13 } 14 15 int N,v[maxn],f[maxn][maxs][maxb],b[maxn],bin[maxb]; 16 17 int gettime(int i,int j,int k){ 18 return (i+k-10)?(v[i+j]|v[i+k-10])-(v[i+j]&v[i+k-10]):0; 19 } 20 21 int main(){ 22 //freopen("","r",stdin); 23 int i,j,k,l; 24 bin[1]=1;for(i=2;i<=8;i++) bin[i]=bin[i-1]<<1; 25 for(int T=rd();T;T--){ 26 N=rd(); 27 for(i=1;i<=N;i++) 28 v[i]=rd(),b[i]=rd(); 29 for(i=N+1;i<=N+10;i++) 30 b[i]=23333; 31 CLR(f,127); 32 f[1][0][9]=0;int ans=1e9+1; 33 for(i=1;i<=N;i++){ 34 for(j=0;j<bin[b[i]+1];j++){ 35 for(k=0;k<=10+b[i];k++){ 36 if(f[i][j][k]>=1e9) continue; 37 int mi=23333333; 38 for(l=1;l<=b[i]&&l<=mi;l++){ 39 if(!(j&bin[l])) f[i][j|bin[l]][l+10]=min(f[i][j|bin[l]][l+10],f[i][j][k]+gettime(i,l,k)); 40 if(!(j&bin[l])) mi=min(mi,l+b[i+l]); 41 } 42 for(l=1;l<=b[i];l++) 43 if(!(j&bin[l])) break; 44 f[i+l][j>>l][10-l]=min(f[i+l][j>>l][10-l],f[i][j][k]+gettime(i,0,k)); 45 if(i+l>N) ans=min(ans,f[i+l][j>>l][10-l]); 46 } 47 } 48 } 49 printf("%d\n",ans); 50 } 51 return 0; 52 }