状压dp(三)——#10174. 「一本通 5.4 练习 3」动物园

题目链接:https://loj.ac/problem/10174
解题思路
挺难想的状压dp题,我们看到只有5是可以状压的,所以我们就从这里入手,设dp[i][j]表示以i为结尾的连续5个围栏动物状态为j时的最多满足人数,以i结尾,举例即i=7,则对应连续5个为7,6,5,4,3。我们发现dp在i时的状态可由在i-1的状态推出,dp[i][j]=max(dp[(i&15)<<1],dp[(i&15)<<1|1])+cal[i][j]。cal[i][j]表示以i为结尾的连续5个围栏动物状态为j时的满足人数,预处理一下即可。i&15表示去掉高位,保留后四位,这样再左移一位,即使i-1为结尾的状态。需要注意的是,这是一个环,我们要保证头尾状态一致,即i=0状态和i=n状态是一样的,因此,i=0时,每次枚举一个可能状态使其为0,使其他状态无限小,最后在i=n时,只用这个状态更新答案。
AC代码

#include <iostream>
#include <stdio.h>
#include <cstring>
#include <algorithm>
using namespace std;
const int N=1e4+5,M=5e4+5;
int n,m,e,f,l;
int dp[N][35];
int cal[N][35];
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;++i)
    {
        scanf("%d%d%d",&e,&f,&l);
        int x,tmp1,tmp2;
        tmp1=0;
        tmp2=0;
        for(int j=1;j<=f;++j)
        {
            scanf("%d",&x);
            tmp1|=(1<<((x-e+n)%n));
        }
        for(int j=1;j<=l;++j)
        {
            scanf("%d",&x);
            tmp2|=(1<<((x-e+n)%n));
        }
        for(int j=0;j<32;++j)
        if(((~j)&tmp1)||(j&tmp2))
        cal[e][j]++;
    }
    int ans=0;
    for(int i=0;i<32;++i)
    {
        memset(dp[0],-0x3f,sizeof(dp[0]));
        dp[0][i]=0;
        for(int j=1;j<=n;++j)
        for(int k=0;k<32;++k)
        dp[j][k]=max(dp[j-1][((k&15)<<1)],dp[j-1][(k&15)<<1|1])+cal[j][k];
        ans=max(ans,dp[n][i]);
    }
    printf("%d\n",ans);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_44491423/article/details/108290216