BZOJ 1226: [SDOI2009]学校食堂Dining

状压DP
然而我一开始没!有!想!粗!来!
只好参(chao)考(xi)神犇的思路。。
大致是酱的
f(i,j,k) f ( i , j , k ) 表示前 i1 i − 1 个人已经吃了饭,且在 i i 之后的状态为 j j 的人也吃了饭(用二进制表示后面的状态),最后吃的那个人是 i i 之后的第 k k
(注意 k k 可以是负数)
然后
如果 j j & 1=1 1 = 1 那么就表明第 i i 个人也是吃了的,所以可以转移到 f(i+1,j>>1,k1) f ( i + 1 , j >> 1 , k − 1 )
否则就枚举下一个吃饭的人,转移到 f(i,j+1<<l,l) f ( i , j + 1 << l , l )
这么看也不是很难吧哈。。


#include
#include
#include
#define g getchar()
#define f(i,j,k) ge[i][j][k+8]
#define ll long long
#define inf 0x3f3f3f3f
using namespace std;
inline ll read(){
    ll x=0,f=1;char ch=g;
    for(;ch<'0'||ch>'9';ch=g)if(ch=='-')f=-1;
    for(;ch>='0'&&ch<='9';ch=g)x=x*10+ch-'0';
    return x*f;
}
inline void out(ll x){
    int a[25],wei=0;
    if(x<0)putchar('-'),x=-x;
    for(;x;x/=10)a[++wei]=x%10;
    if(wei==0){puts("0");return;}
    for(int j=wei;j>=1;--j)putchar('0'+a[j]);
    putchar('\n');
}
int t[1005],Q,ge[1005][257][17],n,b[1005],ans;
int clear(){memset(ge,inf,sizeof(ge));}
inline int sum(int x,int y){return x==0?0:t[x]^t[y];}
//这里说明一下,根据容斥原理,a|b-a&b=a^b,不信可以手算几个
int main(){
//  freopen("","r",stdin);
//  freopen("","w",stdout);
    Q=read();
    for(;Q--;){
        clear();
        n=read();
        for(int i=1;i<=n;++i)t[i]=read(),b[i]=read();
        f(1,0,-1)=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)if(j&1)f(i+1,j>>1,k-1)=min(f(i+1,j>>1,k-1),f(i,j,k));
            else{
                int lim=inf;//lim是限制,就是防止超出了后面人的要求
                for(int l=0;l<=7;++l)
                if(!(j&(1<if(i+l>lim)break;
                    lim=min(lim,i+l+b[i+l]);
                    f(i,j+(1<1<for(int i=-8;i<=7;++i)ans=min(ans,f(n+1,0,i));
        out(ans);
    }
    return 0;
}

你可能感兴趣的:(dp,bzoj,状态压缩,dp,状态压缩)