bzoj1226 [SDOI2009]学校食堂Dining

题目链接:bzoj1226
题目大意:
有n个口味不同的人排队打饭。由于人手不够,食堂每次只能为一个人做菜。做每道菜所需的时间是和前一道菜有关的,若前一道菜的对应的口味是a,这一道为b,则做这道菜所需的时间为(a or b)-(a and b),而做第一道菜是不需要计算时间的。食堂偶尔会不按照大家的排队顺序做菜,以缩短总的进餐时间。队伍中的第i 个同学,最多允许紧跟他身后的Bi 个人先拿到饭菜。小F 想知道在满足所有人的容忍度这一前提下,自己的学校食堂做完这些菜最少需要多少时间。

题解:
状压dp
因为0<=bi<=7,所以最多只用记录当前人的前7个到后7个的状态就好了。
f[i][j][k]表示前i-1个打了饭第i个人还没有打(额也不一定没打)j记录了i~i+7那么多人的打饭情况,k存上个打饭的人与i的距离
如果j表达出第i个人已经打了饭的含义,那么就直接转移到i+1,否则枚举下个打饭的人。

反思:
尽管想到了状压,但没有留意到bi<=7所带来的空间上的方便。

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define maxn 1010

const int inf=1e9;
int v[maxn],b[maxn],f[maxn][310][20];
int mymin(int x,int y){return (x<y)?x:y;}
int main()
{
    //freopen("a.in","r",stdin);
    //freopen("a.out","w",stdout);
    int T,i,j,k,l,n,ans;
    scanf("%d",&T);
    while (T--)
    {
        scanf("%d",&n);
        for (i=1;i<=n;i++)
         scanf("%d%d",&v[i],&b[i]);
        for (i=1;i<=n+1;i++)
         for (j=0;j<1<<8;j++)
          for (k=-8;k<=7;k++)
            f[i][j][k+8]=inf;
        f[1][0][7]=0;
        for (i=1;i<=n;i++)
         for (j=0;j<1<<8;j++)
          for (k=-8;k<=7;k++)
          {
              int kk=k+8;
              if (f[i][j][kk]<inf)
              {
                  if (j&1) f[i+1][j>>1][kk-1]=mymin(f[i+1][j>>1][kk-1],f[i][j][kk]);
                  else
                  {
                    int ls=inf;
                    for (l=0;l<=7 && i+l<=ls;l++)
                     if (!(j&(1<<l)))
                     {
                        ls=mymin(ls,i+l+b[i+l]);
                        if (i+k<=0) f[i][j|(1<<l)][l+8]=mymin(f[i][j|(1<<l)][l+8],f[i][j][kk]);
                        else f[i][j|(1<<l)][l+8]=mymin(f[i][j|(1<<l)][l+8],f[i][j][kk]+(v[i+k]^v[i+l]));
                     }
                  }
              }
          }
        ans=inf;
        for (k=-8;k<=-1;k++)
         ans=mymin(ans,f[n+1][0][k+8]);
        printf("%d\n",ans);
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值