BZOJ 1226: [SDOI2009]学校食堂Dining

本文介绍了一种使用状态压缩动态规划解决特定问题的方法。通过定义f(i,j,k)为前i-1个人已就餐且在i之后的状态为j的人也就餐(二进制表示),最后就餐者为i之后第k个的状态转移方程。文章提供了完整的C++实现代码,并详细解释了状态转移的过程。

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


#include<cstdio>
#include<cstring>
#include<algorithm>
#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)<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 lim=inf;//lim是限制,就是防止超出了后面人的要求
                for(int l=0;l<=7;++l)
                if(!(j&(1<<l))){
                    if(i+l>lim)break;
                    lim=min(lim,i+l+b[i+l]);
                    f(i,j+(1<<l),l)=min(f(i,j+(1<<l),l),f(i,j,k)+sum(i+k,i+l));
                }
            }
        }
        ans=inf;
        for(int i=-8;i<=7;++i)ans=min(ans,f(n+1,0,i));
        out(ans);
    }
    return 0;
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值