ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

P3622 动物园-状压

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_JUDGEfreopen("cjdl.in","r",stdin);freopen("cjdl.out","w",stdout);#endif // ONLINE_JUDGEscanf("%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->1for(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),在约束范围内。

返回列表