[SDOI2009]学校食堂Dining(状压DP)

题意:传送门

题解:设f[i][j][k]为第一个人到第i-1个人都打完了饭,此时j表示第i个人以及i后面7个人的状态,k表示第i+k个人最后打完饭(-8<=k<=7),由于有负数,所以最后统一+8,转移为什么呢?

如果此时j&1为真,那么也就是说第i位这个人打完饭了,就表示第i个人已经打完饭,i之后的7个人中,还没打饭的人就再也不会插入到第i个人前面了,此时状态转移方程为:

f[i+1][j>>1][k-1]=min(f[i+1][j>>1][k-1],f[i][j][k]);

仔细想一番,其实在这种情况下,f[i+1][j>>1][k-1]f[i][j][k]就是同一个意思。

但是如果j&1为假呢?这是转移不到i+1的,就得枚举第i位以及i后面7个人的情况了,此时的动态转移方程为:

f[i][j|(1<<h)][h]=min(f[i][j|(1<<h)][h],f[i][j][h]+time(i+k,i+h));

但是这个转移需要考虑忍耐度的问题,也就是第i位还没吃饭的人不允许后面第b[i]人之后插到他前面,然后通过枚举改变这个最小的限制lim即可,如果i+h>lim,那么直接跳出即可。

附上代码:


#include<bits/stdc++.h>

using namespace std;

const int maxn=1e3+5;
const int inf=0x3f3f3f3f;

inline int read()
{
    register int x=0,t=1;
    register char ch=getchar();
    while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
    if(ch=='-'){t=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-48;ch=getchar();}
    return x*t;
}

int n,t[maxn],b[maxn],f[maxn][1<<8][20];

int main()
{
    int T;
    scanf("%d",&T);
    while(T--){
        n=read();
        for(int i=1;i<=n;i++){
            t[i]=read();b[i]=read();
        }
        memset(f,inf,sizeof(f));
        f[1][0][7]=0;
        for(int i=1;i<=n;i++){
            for(int j=0;j<(1<<8);j++){
                for(int k=-8;k<=7;k++){
                    if(f[i][j][k+8]!=inf){
                        if(j&1){
                            f[i+1][j>>1][k+7]=min(f[i+1][j>>1][k+7],f[i][j][k+8]);
                        }else{
                            int lim=inf;
                            for(int h=0;h<=7;h++){
                                if(!((j>>h)&1)){
                                    if(i+h>lim){
                                        break;
                                    }
                                    lim=min(lim,i+h+b[i+h]);
                                    f[i][j|(1<<h)][h+8]=min(f[i][j|(1<<h)][h+8],f[i][j][k+8]+(i+k?(t[i+k]^t[i+h]):0));
                                }
                            }
                        }
                    }
                }
            }
        }
        int res=inf;
        for(int k=0;k<=8;k++){
            res=min(res,f[n+1][0][k]);
        }
        printf("%d\n",res);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/zhouzi2018/article/details/85670778