P3622 动物园-状压

P3622 动物园-状压

[APIO2007] 动物园

题目大意

问题描述:
有一个环形动物园,共有 N 个围栏(环形排列),每个围栏里有一种动物。有 C 个小朋友,每个小朋友会从某个围栏 E 开始,连续看到 5 个围栏(顺时针方向)。
每个小朋友有喜欢的动物和害怕的动物(都在他看到的 5 个围栏中)。

小朋友高兴的条件是:

  1. 至少有一个他害怕的动物被移走;
  2. 至少有一个他喜欢的动物没有被移走。

目标: 选择移走哪些动物,使得高兴的小朋友数量最多


输入格式

N C
E1 F1 L1 X1 X2 ... XF1 Y1 Y2 ... YL1
  • 第 1 行N(围栏数,10 ≤ N ≤ 10^4),C(小朋友数,1 ≤ C ≤ 5×10^4)。
  • 接下来 C 行:每行描述一个小朋友:
    • E:该小朋友看到的第一个围栏编号(1 ≤ E ≤ N)。
    • F:害怕的动物数。
    • L:喜欢的动物数。
    • 接着 F 个整数:害怕的动物所在围栏编号。
    • 接着 L 个整数:喜欢的动物所在围栏编号。
    • 所有 XY 都是该小朋友能看到的 5 个围栏中的,且互不相同。

输出格式

一个整数:最多能让多少个小朋友高兴


解题思路

关键观察

  1. 每个小朋友只能看到 5 个围栏,这个数字很小,提示我们可以用状态压缩
  2. 相邻的小朋友看到的围栏有重叠部分(4 个),因此状态可以传递。
  3. 由于是环形,首尾状态必须一致(最后 4 个围栏与开头 4 个围栏对应)。

状态设计

dp[i][j] 表示处理到第 i 个围栏,且从第 i 个围栏开始的 5 个围栏的状态为 j(二进制表示,1 表示保留,0 表示移走)时,最多能高兴的小朋友数。

状态转移

当前状态的后 4 位必须等于前一个状态的前 4 位(因为重叠):

dp[j][k] = max(dp[j-1][(k&15)<<1], dp[j-1][(k&15)<<1|1]) + cnt[j][k]

其中 15 的二进制是 1111,用于取后 4 位。

预处理

对于每个小朋友,我们记录他站在位置 st 时,对 5 个围栏的每种状态 j 是否高兴:

  • 如果 (害怕的动物集合) ∩ (移走集合) ≠ ∅(喜欢的动物集合) ∩ (保留集合) ≠ ∅,则该小朋友高兴。
  • 预处理 cnt[st][j] 表示在位置 st,状态为 j 时高兴的小朋友数。

环形处理

枚举初始状态 i(0 到 31),设置 dp[0][i] = 0,然后进行 DP。最后检查 dp[n][i] 是否与初始状态一致,取最大值。


代码实现

#include <bits/stdc++.h>
#define int long long
using namespace std;
constexpr int maxn = 5e4+10;
constexpr int maxm = 3e6+10;
constexpr int INF = 0x3f3f3f3f3f3f3f3f;

int n,m,ans;
int cnt[maxn][35];
int dp[maxn][35];

signed main()
{
    #ifndef ONLINE_JUDGE
    freopen("cjdl.in","r",stdin);
    freopen("cjdl.out","w",stdout);
    #endif // ONLINE_JUDGE

    scanf("%lld%lld",&n,&m);
    for(int i=1,st,f,l ;i<=m;++i)// st,fear,love
    {
        scanf("%lld%lld%lld",&st,&f,&l);
        int fear=0;
        int love=0;
        for(int j=1,pos ;j<=f;++j)
        {
            scanf("%lld",&pos);
            fear |= (1<<(pos-st+n)%n);// 高位离st远
        }
        for(int j=1,pos ;j<=l;++j)
        {
            scanf("%lld",&pos);
            love |= (1<<(pos-st+n)%n);
        }
        for(int j=0;j<32;++j)// 31==0b11111   0-zou 1-liu
        {
            if((fear & ~j) || (love & j))
            {
                ++cnt[st][j];
            }
        }
    }

    for(int i=0;i<32;++i)
    {
        memset(dp,128,sizeof(dp[0])*(n+1));
        dp[0][i]=0;// 0->1
        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])+cnt[j][k];
                // 15=0b1111,qu hou 4 wei
                // 取当前的近4位,枚举上个位置的第一位
            }
        }
        ans=max(ans,dp[n][i]);// 1 from 0_i ,n->1 <=> n==0_i
    }
    printf("%lld",ans);

    return 0;
}

复杂度分析

  • 时间复杂度:O(N × 32 × 2) = O(64N),可以通过。
  • 空间复杂度:O(N × 32),在约束范围内。

posted @ 2025-11-10 14:06  玖玮  阅读(4)  评论(0)    收藏  举报