[状压dp] 动物园题解
Posted zero_orez6
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[状压dp] 动物园题解相关的知识,希望对你有一定的参考价值。
动物园
P2911 动物园 题解
题意
有N个成环形的房间,C个小朋友,对于每个小朋友他们能看到E - E+4共五个房间,每个小朋友有F个喜欢的房间和L个讨厌的房间,当满足以下两个条件其中之一时,小朋友就会开心:
-
至少有一个他害怕的房间被移走;
-
至少有一个他喜欢的房间没被移走。
可以移走若干个房间,问最多能令多少个小朋友开心。
分析
dp式
对于任一个小朋友能够看到的五个房间,我们用0和1来表示这个房间是否撤走,而每个小朋友能看到的5个房间恰好能够作为状压dp的变量,用一个五位的二进制数s来表示任一位置$ i - i+4 $的状态
用f[i][s]表示当$ i-i+4 这 五 个 房 间 的 状 态 为 s 时 , 能 够 最 多 让 多 少 小 朋 友 开 心 , 那 么 对 于 任 意 时 刻 的 f [ i ] [ s ] 肯 定 由 f [ i − 1 ] [ s ] 所 转 移 过 来 的 , 也 就 是 说 从 这五个房间的状态为s时,能够最多让多少小朋友开心,那么对于任意时刻的f[i][s]肯定由f[i-1][s]所转移过来的,也就是说从 这五个房间的状态为s时,能够最多让多少小朋友开心,那么对于任意时刻的f[i][s]肯定由f[i−1][s]所转移过来的,也就是说从 (i-1) - (i+3) $的房间已经确定好了状态,只差i+4未确定状态,由此可以得出状态转移式为:
f[i][s]=max(f[i-1][(s&15)<<1],f[i-1][(s&15)<<1|1])+sum[i][s]
其中sum[i][s]表示当i - i+4这五个房间的状态为s时能够使多少个小朋友开心。初始状态为 $ f[0][s]=0 $ ,s为当前所枚举的状态。
为什么是s&15呢 ?
因为从i-1 - i+3的房间已经确定好了状态,我们现在要确定第i+4位的状态,也就是取i-1 - i+3后四位的状态,15(10)=1111(2),通过&操作取后四位,再左移判断第i+4位。
sum[i][s]如何去求呢?
对于任一个小朋友,其输入为 $ E\\ F\\ L\\ x_1\\ x_2…\\ x_F \\ y_1\\ y_2\\ …\\ y_L $
我们用两个五位二进制数like和hate来表示当前小朋友所能够看到的房间$ E - E+4 的 状 态 s 但 注 意 此 题 目 所 有 房 间 成 环 形 , 在 处 理 时 的状态s 但注意此题目所有房间成环形,在处理时 的状态s但注意此题目所有房间成环形,在处理时 a-x $有可能为负数,所以应写成:
like=like|(1<<(a-x+n)%n)
在每一个小朋友的数据输入完后,枚举每个状态s
if((s&hate)||(~s&like)) sum[E][s]++;
表示若满足两个条件中的任一个,sum[E][s]++。
答案的统计
在dp的过程中,我们肯定先枚举第0位的状态,而f[i][s]表示当$ i-i+4 $这五个房间的状态为s时,能够最多让多少小朋友开心,所以答案最后为
$ ans=MAX_{1 - n} f[n][i] $
code
#include<bits/stdc++.h>
using namespace std;
const int N=1e4+86,MAX=1e8+86;
int n,c,like,hate,sum[N][50];
int main()
{
freopen("zoo.in","r",stdin);
freopen("zoo.out","w",stdout);
cin>>n>>c;
for(int i=1;i<=c;i++)
{
int x,y,z,a;
like=0;hate=0;//初始化
cin>>x>>y>>z;
for(int j=1;j<=y;j++)
{
cin>>a;
hate=hate|(1<<((a-x+n)%n));//处理五位二进制数hate
}
for(int j=1;j<=z;j++)
{
cin>>a;
like=like|(1<<((a-x+n)%n));//处理like
}
for(int j=0;j<(1<<5);j++)//枚举所有状态
{
if((j&hate)||(~j&like)) sum[x][j]++;//判断是否满足两个条件其中之一
}
}
int f[N][50],ans=0;
for(int i=0;i<(1<<5);i++)//枚举第0位的状态
{
memset(f[0],-1086,sizeof(f[0]));//将f[0][1~n]全部赋值为极小值
f[0][i]=0;//初始化f[0][i]
for(int j=1;j<=n;j++)
{
for(int k=0;k<(1<<5);k++)
{
f[j][k]=max(f[j-1][(k&15)<<1],f[j-1][(k&15)<<1|1])+sum[j][k];//dp式
}
}
ans=max(ans,f[n][i]);//更新答案
}
cout<<ans<<endl;
return 0;
}
以上是关于[状压dp] 动物园题解的主要内容,如果未能解决你的问题,请参考以下文章
bzoj1151: [CTSC2007]动物园zoo 状压dp