题目链接: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;
}