[SDOI2009] 学校食堂

题目简述:小 F 的学校在城市的一个偏僻角落,所有学生都只好在学校吃饭。学校有一个食堂,虽然简陋,但食堂大厨总能做出让同学们满意的菜肴。当然,不同的人口味也不一定相同,但每个人的口味都可以用一个非负整数表示。 由于人手不够,食堂每次只能为一个人做菜。做每道菜所需的时间是和前一道菜有关的,若前一道菜的对应的口味是 \(a\),这一道为 \(b\),则做这道菜所需的时间为 \((a\operatorname{or}b)-(a\operatorname{and}b)\),而做第一道菜是不需要计算时间的。其中,\(\operatorname{or}\)\(\operatorname{and}\) 表示整数逐位或运算及逐位与运算.

学生数目相对于这个学校还是比较多的,吃饭做菜往往就会花去不少时间。因此,学校食堂偶尔会不按照大家的排队顺序做菜,以缩短总的进餐时间。

虽然同学们能够理解学校食堂的这种做法,不过每个同学还是有一定容忍度的。也就是说,队伍中的第 \(i\) 个同学,最多允许紧跟他身后的 \(B_i\) 个人先拿到饭菜。一旦在此之后的任意同学比当前同学先拿到饭,当前同学将会十分愤怒。因此,食堂做菜还得照顾到同学们的情绪。 现在,小 F 想知道在满足所有人的容忍度这一前提下,自己的学校食堂做完这些菜最少需要多少时间。

Analysis:

\(f_{i,S,p}\) 表示前 \(i-1\) 个同学均已拿到饭菜,第 \(i\) 人未拿到饭菜,在 \(i\) 后面的拿到饭菜的同学集合为 \(S\),最后一个拿到饭菜的同学为 \(i+p\) 的方案数。\(S\) 用二进制表示,由于 \(B_i\le7\),所以 \(S<2^7\)。由于 \(p\) 可以为负数,所以要加 \(8\) 变为非负整数,然后就是刷表法计算,最终答案为 \(\min\{f_{n+1,0,p}\}\)

int re[1<<8];
int T,n,Ta[1025],B[1025];
int f[1019][1<<8][16];
bool judge(int x,int S,int t){
    for(int i=0;i<t;i++)
        if((!(S&(1<<i)))&&B[x+i+1]<t-i)
            return false;
    return true;
}
inline string str(int S,int len){
    string s="";
    for(;len--;S>>=1)s+='0'+(S&1);
    return s;
}
inline void Out(int x,int S,int p){
    cout<<x<<' '<<str(S,B[x])<<' '<<p-8<<' '<<f[x][S][p];
}
int main(){
#ifdef LOCAL
    freopen("a.in","r",stdin);
    freopen("a.out","w",stdout);
#endif
    for(int i=0;i<8;i++)re[1<<i]=i;
    for(scanf("%d",&T);T--;){
        scanf("%d",&n);
        for(int i=1;i<=n;i++)scanf("%d%d",&Ta[i],&B[i]);
        for(int i=1;i<=n+10;i++)memset(f[i],0x7f,sizeof f[i]);
        for(int i=n+1;i<=n+10;i++)B[i]=0,Ta[i]=0;
        f[2][0][7]=0;
        for(int i=0;i<B[1];i++)
            if(judge(1,0,i))f[1][1<<i][8+i+1]=0;
        for(int x=1;x<=n;x++)for(int S=0;S<(1<<B[x]);S++)
            for(int p=0;p<=15;p++)if(f[x][S][p]<1e7){
                //Out(x,S,p),cout<<endl;
                int ss=(1<<B[x])-1;ss^=S;
                for(;ss;ss-=ss&-ss){
                    int t=re[ss&-ss];
                    if(judge(x,S,t))
                        f[x][S|(1<<t)][9+t]=min(f[x][S|(1<<t)][9+t],f[x][S][p]+(Ta[x+p-8]^Ta[x+t+1]));
                }
                for(int i=0;i<=B[x]+1;i++)if((S&(1<<i))==0){
                    f[x+1+i][S>>(1+i)][8-i-1]=min(f[x+1+i][S>>(1+i)][8-i-1],f[x][S][p]+(Ta[x+p-8]^Ta[x]));
                    //Out(x,S,p), cout<<"->", Out(x+1+i,S>>(1+i),8-i-1), cout<<endl;
                    break;
                }
            }
        int ans=1e9;
        for(int i=0;i<=15;i++)ans=min(ans,f[n+1][0][i]);
        cout<<ans<<endl;
    }
    return 0;
}
posted @ 2025-06-19 19:59  fzrcy  阅读(13)  评论(0)    收藏  举报