[SDOI2009] 学校食堂Dining

题目描述:

QAQ…

题目分析:

状压DP…
观察到一个人的忍耐程度很小,可以状压…
f[i][j][k]表示[1 – i-1] 吃完了 i 之后 状态 j 中 为 1的人也吃饭了,且最后一个吃饭的人为 i+k 的最小代价
k可以为负数

转移的时候枚举 j
如果j&1不为0,那么证明i这个人吃饭了 可以转移到 f[i+1][j>>1][k-1] 去
如果为 0 那么证明 i 没吃饭 那么就枚举下一个吃饭的 转移到 f[i][j+(1<

题目链接:

BZOJ 1226
Luogu 2157

Ac 代码:

#include <cstring>
#include <algorithm>
#include <cstdio>
#define f(i,j,k) dp[i][j][k+8] 
#define inf 0x3f3f3f3f
#define int long long
int dp[1101][1<<9][20];
int t[1101],b[1101];
int n;
inline int cal(int x,int y){return x==0?0:t[x]^t[y];}
inline void work()
{
    scanf("%lld",&n);
    for(int i=1;i<=n;i++) scanf("%lld%lld",&t[i],&b[i]);
    memset(dp,inf,sizeof(dp));
    f(1,0,-1)=0;
    for(int i=1;i<=n;i++)
     for(int j=0;j<1<<8;j++)
      for(int k=-8;k<=7;k++)
       if(f(i,j,k)<inf)
       {
         if(j&1) f(i+1,j>>1,k-1)=std::min(f(i+1,j>>1,k-1),f(i,j,k));
         else
         {
            int minx=inf;
            for(int l=0;l<=7&&i+l<=minx;l++)
            if(!(j&(1<<l)))
            {
                minx=std::min(minx,i+l+b[i+l]);
                f(i,j+(1<<l),l)=std::min(f(i,j+(1<<l),l),f(i,j,k)+cal(i+k,i+l));
            }
         }
       } 
    int ans=inf;
    for(int i=-8;i<=7;i++) ans=std::min(ans,f(n+1,0,i));
    printf("%lld\n",ans);
}
signed main()
{
    int t;
    scanf("%lld",&t);
    while(t--) work();
    return 0;
} 

猜你喜欢

转载自blog.csdn.net/qq_35914587/article/details/80264303