luogu 2157 状压dp

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=0,f=1;char ch=getchar();
    while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
    while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
    return x*f;}

const int N=1050;
const int inf=0x3f3f3f3f;

int n,t[N],b[N],dp[N][1<<8][20];
#define f(i,j,k) dp[i][j][k+8]
inline void sudo(){ n=read();
    rep(i,1,n) t[i]=read(),b[i]=read();
    memset(dp,inf,sizeof dp);
    f(1,0,-1)=0;
    rep(i,1,n)rep(j,0,(1<<8)-1)
    rep(k,-8,7)if(f(i,j,k)!=inf)
        if(j&1) f(i+1,j>>1,k-1)=min(f(i+1,j>>1,k-1),f(i,j,k));
        else{
            int r=inf;
            rep(h,0,7)if(!((j>>h)&1)){//在这个人之前的未打饭的人 
                if(i+h>r) break;
                r=min(r,i+h+b[i+h]);
                //距离i的最远距离
                //判断i+k就是判断上一次是否是起点 
                f(i,j|(1<<h),h)=min(f(i,j|(1<<h),h),f(i,j,k)+(i+k?(t[i+k]^t[i+h]):0)); 
            }
        }
    int ans=inf; rep(k,-8,-1)
    ans=min(ans,f(n+1,0,k));
    printf("%d\n",ans);return;}

int main(){
    int T=read();while(T--) sudo();return 0;}

完结撒花

posted @ 2018-10-19 07:54  ASDIC减除  阅读(163)  评论(0编辑  收藏  举报