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

来源:互联网 发布:mac bluestack模拟器 编辑:程序博客网 时间:2024/05/21 10:54

bzoj1226 [SDOI2009]学校食堂Dining

原题地址:http://www.lydsy.com/JudgeOnline/problem.php?id=1226

题意:
T组数据。
有N个人排队吃饭,每个人有a,b两个属性,要以某种顺序为这N个人做菜,考虑目前这个人是i,他的前一个人j,那么让他吃饭的时间就是(a[i] | a[j]) – (a[i] & a[j])(如果是第一个吃饭的人,那么代价就是0),按原本顺序队伍中的第i 个同学,最多允许紧跟他身后的Bi 个人先拿到饭菜(就是说原本顺序的人 k,k > i + b[i] ,不能在事i之前完成)。
求让N个人吃上饭的最小时间。

数据范围
对于30%的数据,满足1 ≤ N ≤ 20。
对于100%的数据,1 ≤ T ≤ 5,1 ≤ N ≤ 1,000,0 ≤ Ai ≤ 1,000,0 ≤ Bi ≤ 7

题解:
可以看到部分数据1 ≤ N ≤ 20,对于这种情况,能够想象出的就是用状压来记录,dp[i][s]表示状态为s的人吃过了,当前吃的人是i。

由于给了B[ ],又发现0 ≤ Bi ≤ 7,可以想到类似的状压。

dp[i][s][j]表示,处理到第i位,1~i-1的人都吃过了,对于原来排队在i之后的7个人加上i本身,共8位,此时的状态为s,上一个吃饭的人是j,的最小时间。 但是1 ≤ N ≤ 1000,空间开不下。
观察可知由于0 ≤ Bi ≤ 7,那么上一个吃饭的人的范围就只是 [ i-8,i+7 ](是j的下一个的下7个、i+7个在i前面吃饭)

于是j表示上一个吃饭的与i的相对位置[-8,+7],因为有负数。统一加个8。

对于dp[i][s][j],可以得到转移:
若第i位已经吃过(s&1>0) ,直接转移到下一位
dp[i+1][s>>1][j-1]=min(dp[i+1][s>>1][j-1],dp[i][s][j])

否则枚举s中没吃过的人k,
dp[i][s|(1<< k)][k]=min(dp[i][s|(1<< k)][k],dp[i][s][j]+w(i+j,i+k))
注意每次更新枚举k的限制,保证对于这些人也满足在他前面的只有位置小于等于b的。

代码:

#include<cstdio>#include<iostream>#include<cstring>#include<algorithm>#define LL long longusing namespace std;const int N=1005;const int inf=0x3f3f3f3f;const int P=8;const int top=(1<<8)-1;int dp[N][(1<<8)+10][20];int T,n,a[N],b[N];int w(int x,int y){    if(x==0) return 0; else return a[x]^a[y];}int main(){    scanf("%d",&T);    while(T--)    {        scanf("%d",&n);        for(int i=1;i<=n;i++) {scanf("%d%d",&a[i],&b[i]);b[i]=min(b[i],n-i);}        memset(dp,0x3f,sizeof(dp));        dp[1][0][-1+P]=0;        for(int i=1;i<=n;i++)        for(int s=0;s<=top;s++)        for(int j=-8;j<=7;j++)        {            if(dp[i][s][j+P]<inf)            {                if(s&1)                 { dp[i+1][s>>1][j-1+P]=min(dp[i+1][s>>1][j-1+P],dp[i][s][j+P]); continue;}                int lim=7;                for(int k=0;k<=lim;k++)                if(!((1<<k)&s))                {                    dp[i][s|(1<<k)][k+P]=min(dp[i][s|(1<<k)][k+P],dp[i][s][j+P]+w(i+j,i+k));                    lim=min(lim,k+b[i+k]);                }               }           }        int ans=inf;        for(int i=-8;i<=-1;i++) ans=min(ans,dp[n+1][0][i+P]);        printf("%d\n",ans);    }    return 0;}
原创粉丝点击