(状压dp)[APIO2007] 动物园

[APIO2007]动物园

link

题意

环形动物园里的动物具有两种性质,有人喜欢有人害怕,如果游客所能看见的5格范围内有自己喜爱的动物或没有自己害怕的动物则该游客处于满意的状态。求移走若干数量的动物后满意状态的游客数量的最大值。

思路

由于在状压板块,考虑状压(?)。n与c范围太大,无法压。观察到每个人视野范围只有5,可以压(怎么可能观察得到qwq)。
设计状态:dp数组储存每个状态下满意人数的最大值。dp数组第一维为动物所在的格数,第二维为包括当前格并往后4格总共5格的状态,用二进制表示。
预处理每格每个状态下能满意的人的个数。根据输入进行或运算得到用二进制表示的fear与love。枚举状态,当love与该状态有同为1的位,显然有人满足,该状态能满足的人数加一。该状态取反,则不存在的动物位为1,若与fear取与,则存在去掉让人害怕的动物而使人满意。
当前格数可由上一格推出,为上一格第一位为0或1时的最大值加上该格该状态下能满意的人数。由于二进制表示与实际格数可为反向的(注意预处理与转移状态的方向应相同),所以可以方便地简化dp方程。据此,我们能轻松(bushi)得出状态转移方程

\[dp[i][j]=max(dp[i-1][\text{(j&15)<<15],ff[i-1][((j&15)<<1)|1])+num[i][j]} \]

由于第0个状态和第n个状态是相同的,故可枚举初始(结束)状态,取每个状态下dp[n][s]的最大值即为所求答案。
由于所有都由dp[0]推出,故可以仅在每个状态下将dp[0]赋为极小值,并将dp[0][s]赋为0。

#include<bits/stdc++.h>
using namespace std;
int n,c,e,f,l,num[10010][1<<5],ff[10010][1<<5],ans;
int main(){
	std::ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
	cin>>n>>c;
	for(int i=1;i<=c;i++){
		cin>>e>>f>>l;
		int fe=0,lo=0,p;
		for(int j=1;j<=f;j++) cin>>p,fe|=(1<<((p-e+n)%n));
		for(int j=1;j<=l;j++) cin>>p,lo|=(1<<((p-e+n)%n));
		for(int j=0;j<(1<<5);j++)
		  if(((~j)&fe)||(lo&j)) num[e][j]++;
	}
	for(int i=0;i<(1<<5);i++){
		memset(ff[0],-0x3f,sizeof(ff[0]));
		ff[0][i]=0;
		for(int j=1;j<=n;j++)
		  for(int k=0;k<(1<<5);k++)
		    ff[j][k]=max(ff[j-1][(k&15)<<1],ff[j-1][((k&15)<<1)|1])+num[j][k];
		 ans=max(ans,ff[n][i]);   
	}
	cout<<ans;//????????
	return 0;
}
posted @ 2025-08-09 15:38  _dlwlrma  阅读(7)  评论(0)    收藏  举报