SDOI2009 学校食堂(状压)

题意

https://www.luogu.org/problemnew/show/P2157

思路

不仅状压难想到,暴力分也极不和善。首先 B i B_i Bi 的值不超过 7 7 7,应该条件反射的想到状压 D P DP DP,不妨设 d p i , j , k dp_{i,j,k} dpi,j,k 表示区间 [ 1 , i − 1 ] [1,i-1] [1,i1] 内的同学结束用餐, [ i , i + 7 ] [i,i+7] [i,i+7] 区间内的同学用餐情况为 j j j,其中最后一个吃饭的同学是 i + k i+k i+k 其中 k ∈ [ − 8 , 7 ] k\in[-8,7] k[8,7],转移就比较显然了。
{ chk_min ( d p i + 1 , j > > 1 , k − 1 , d p i , j , k )  当 j&1=1 chk_min ( d p i , j ∣ ( 1 < < l ) , l , d p i , j , k ) \begin{cases} \text{chk\_min}(dp_{i+1,j>>1,k-1},dp_{i,j,k})\text{ 当 j\&1=1}\\ \text{chk\_min}(dp_{i,j|(1<<l),l},dp_{i,j,k}) \end{cases} {chk_min(dpi+1,j>>1,k1,dpi,j,k)  j&1=1chk_min(dpi,j(1<<l),l,dpi,j,k)
前式表示 i i i 的移动,后式表示有多了一个吃饭的人,按照式子计算。
另外 d p dp dp 数组出现负数时, define \text{define} define 是个不错的选择。

代码

#include
#define FOR(i,x,y) for(int i=(x),i##END=(y);i<=i##END;++i)
#define DOR(i,x,y) for(int i=(x),i##END=(y);i>=i##END;--i)
typedef long long LL;
using namespace std;
template<typename T>bool chk_max(T &x,T y){return x<y?x=y,1:0;}
template<typename T>bool chk_min(T &x,T y){return y<x?x=y,1:0;}
const int N=1003;
int T[N],B[N],dp[N][(1<<8)+3][20],n;
#define dp(x,y,z) dp[x][y][(z)+8]

int Cost(int x,int y)
{
    if(x==0||y==n+1)return 0;
    else return T[x]^T[y];
}
 
int main()
{
    int C;
    scanf("%d",&C);
    while(C--)
    {
        int ans=2e9;
        scanf("%d",&n);
        FOR(i,1,n)scanf("%d%d",&T[i],&B[i]);
        memset(dp,0x3f,sizeof(dp));
        dp(1,0,-1)=0;
        FOR(i,1,n)FOR(j,0,(1<<8)-1)FOR(k,-8,7)if(dp(i,j,k)<1e9)
        {
            if(j&1){chk_min(dp(i+1,j>>1,k-1),dp(i,j,k));continue;}
            int maxer=1e9;
            FOR(l,0,7)if(~j&(1<<l))
            {
                if(i+l>maxer)break;
                chk_min(maxer,i+l+B[i+l]);
                chk_min(dp(i,j|(1<<l),l),dp(i,j,k)+Cost(i+k,i+l));
            }
        }
        FOR(i,-8,-1)chk_min(ans,dp(n+1,0,i));
        printf("%d\n",ans);
    }
    return 0;
}

你可能感兴趣的:(题目)