BZOJ1226 SDOI2009学校食堂(状压dp)

  由于Bi<=7,考虑状压。

  如果考虑前i个位置的话,状态里需要压入前7个人后7个人,显然是跑不动的。

  那么改成考虑前i个人。于是设f[i][j][k]表示前i个人都已吃完饭,i+1后面7个人的吃饭状态为j,最后一个吃饭的人是k的答案。转移时考虑下一个吃饭的是谁即可。

  a|b-a&b=a^b。当然没什么用。

  各种情况需要考虑的非常清楚。写的跟我一样丑的话就比较难搞了。

#include<iostream> 
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
int read()
{
    int x=0,f=1;char c=getchar();
    while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();}
    while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
    return x*f;
}
#define N 1010
int T,n,a[N],b[N],f[N][1<<7][16],lg2[1<<7|1],l[7];
int main()
{
#ifndef ONLINE_JUDGE
    freopen("bzoj1226.in","r",stdin);
    freopen("bzoj1226.out","w",stdout);
    const char LL[]="%I64d\n";
#else
    const char LL[]="%lld\n";
#endif
    T=read();
    for (int i=0;i<=7;i++) lg2[1<<i]=i;
    while (T--)
    {
        int n=read();
        for (int i=1;i<=n;i++) a[i]=read(),b[i]=read();
        memset(f,42,sizeof(f));
        f[0][0][7]=0;
        for (int i=0;i<n;i++)
            for (int j=0;j<(1<<min(7,n-i-1));j++)
            {
                l[0]=min(i+1+b[i+1],n);
                for (int k=1;k<7;k++)
                if (!(j&(1<<k-1))) l[k]=min(l[k-1],i+k+1+b[i+k+1]);
                else l[k]=l[k-1];
                for (int k=0;k<16;k++)
                if (k<=7&&i+k-7>=0||k>=9&&(j&(1<<k-9)))
                {
                    f[i+1+lg2[j+1&-(j+1)]][j>>lg2[j+1&-(j+1)]+1][7-lg2[j+1&-(j+1)]]=
                    min(f[i+1+lg2[j+1&-(j+1)]][j>>lg2[j+1&-(j+1)]+1][7-lg2[j+1&-(j+1)]],f[i][j][k]+(i+k-7?(a[i+k-7]^a[i+1]):0));
                    for (int x=1;x<=7;x++)
                    if (!(j&(1<<x-1))&&i+x+1<=l[x-1])
                    f[i][j|(1<<x-1)][8+x]=min(f[i][j|(1<<x-1)][8+x],f[i][j][k]+(i+k-7?(a[i+k-7]^a[i+x+1]):0));
                }
            }
        for (int i=1;i<8;i++) f[n][0][0]=min(f[n][0][0],f[n][0][i]);
        cout<<f[n][0][0]<<endl;
    }
    return 0;
}

 

posted @ 2018-09-13 20:03  Gloid  阅读(130)  评论(0编辑  收藏  举报