这一道题,我也是搞了很久才搞懂的(也就两个多小时)。

感谢Rayment大佬的题解!

我们进入正题。

对于一个笼子里的动物,我们可以选择撤走或不撤走,可以用0和1来表示,很容易就想到二进制,想到状压dp(为什么先选dp,看数据想想吧)。

观察题面,我们可以发现,小朋友最多可达五万人(动物园容得下吗...)!太大了,肯定不能来做状压dp。

不过,正当我们努力寻找更小的可以用来状压dp变量时,去忽略了题面上的一点:

每个小朋友站在大围栏圈的外面,可以看到连续的 5 个围栏

5,这个数字很小,可以用来状压!

所以,我们可以列出状态转移方程:

f[j][s]=max(f[j-1][(s&15)<<1],f[j-1][(s&15)<<1|1])+num[j][s];

(f[i][s]表示{i,i+1,i+2,i+3,i+4}这五个围栏状态为s时,开心的小朋友数)

可能看不懂,我们来剖析一下这个状态转移方程


首先,为什么这里是 &15 ,而不是其他的变量呢?

转换成二进制,我们惊奇的发现:

                 15(10)=01111(2)

15的用处就出来了:取后四位

继续来看:

         |-------------------------------|
|第几个围栏:|7 |6 |5 |4 |3 |2 |1 |
|----------|--|--|--|--|--|--|--|
|此围栏状态:|0 |1 |0 |1 |0 |0 |1 |
|----------|--|--|--|--|--|--|--|
|当前i :|1 |1 |1 |1 |1 | |
|-------------------------------|

i开始的状态是10100,对应的是{3,4,5,6,7}。状态的后四位是0100,我们要考虑的就是i-1开始的前四位是0100的最优状态。

所以考虑是在0100后面放0还是1(有五个围栏)

在后面放,理所当然,i-1看到了{2,3,4,5,6}。可用来转移。

最后一点:num[i][s]为已经预处理出来的数组,它的意思为:

i,i+1,i+2,i+3,i+4 几个围栏的状态为s是开心的小朋友数


  • 关于num的预处理

上文已经提到,num是预处理出来的数组,那么怎么来预处理呢?

我们先来看代码:

scanf("%d%d",&n,&m);
for(register int i=1;i<=m;++i)
{
int a,b,c,t,like=0,fear=0;
scanf("%d%d%d",&a,&b,&c);
for(register int j=1;j<=b;++j)
{scanf("%d",&t);t=(t-a+n)%n;fear|=1<<t;}
//l表示这五个围栏中这个小朋友害怕的动物的状态
for(register int j=1;j<=c;++j)
{scanf("%d",&t);t=(t-a+n)%n;like|=1<<t;}
//like表示这五个围栏中这个小朋友喜欢的动物的状态
for(register int j=0;j<32;++j)
if(((j&fear)||(~j&like)))++num[a][j];
}

来慢慢解释:

先看这一句:

      scanf("%d",&t);t=(t-a+n)%n;fear|=1<<t;

我们来看看过程(就以第一个样例中的最后一个小朋友当例子):

   Ka-Shu可以看到的围栏:{12,13,14,1,2}
起初,fear是等于0的:
fear: 0(10) 00000(2)
然后,读入了12,12是Ka-Shu可以看到的所有围栏中的第一个
于是:
1<<(1-1)=1;1(10)=00001(2)
fear: 0|1=1(10) 00001(2)
读入13,是第二个。
于是:
1<<(2-1)=2;2(10)=00010(2)
fear: 1|2=3(10) 00011(2)
读入2,是第五个。
于是:
1<<((5-1)=32;32(10)=10000(2)
fear: 3|32=35(10) 10011(2)
所以,我们得到了状态:10011(2),35(10)

读取like状态的那一句同上。

再看这一句:

             if(((j&fear)||(~j&like)))

j的每一个二进制位上的 0/1 表示 没撤走/撤走。

再看题目:

  • 至少有一个他害怕的动物被移走

  • 至少有一个他喜欢的动物没被移走

联系一下上面的if,是不是想通了?

dp可以愉快的开始了!

memset(f[0],128,sizeof(f[0]));//预处理为极小数
f[0][i]=0;
for(register int j=1;j<=n;++j)//枚举每一行
for(register int s=0;s<32;++s)//枚举这一行的状态
f[j][s]=max(f[j-1][(s&15)<<1],f[j-1][(s&15)<<1|1])+num[j][s];
if(ans<f[n][i])ans=f[n][i];

综合以上,我们基本上各方面都搞懂了,接下来,只剩下拼在一起了

AC代码:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=50010;
int n,m,ans,f[N][40],num[N][40];
void input()
{
scanf("%d%d",&n,&m);
for(register int i=1;i<=m;++i)
{
int a,b,c,t,like=0,fear=0;
scanf("%d%d%d",&a,&b,&c);
for(register int j=1;j<=b;++j)
{scanf("%d",&t);t=(t-a+n)%n;fear|=1<<t;}
//l表示这五个围栏中这个小朋友害怕的动物的状态
for(register int j=1;j<=c;++j)
{scanf("%d",&t);t=(t-a+n)%n;like|=1<<t;}
//like表示这五个围栏中这个小朋友喜欢的动物的状态
for(register int j=0;j<32;++j)
if(((j&fear)||(~j&like)))++num[a][j];
}return;
}
int main()
{
input();
for(register int i=0;i<32;++i)
{
memset(f[0],128,sizeof(f[0]));f[0][i]=0;
for(register int j=1;j<=n;++j)//枚举每一行
for(register int s=0;s<32;++s)//枚这一行的状态
f[j][s]=max(f[j-1][(s&15)<<1],f[j-1][(s&15)<<1|1])+num[j][s];
if(ans<f[n][i])ans=f[n][i];
}printf("%d\n",ans);
return 0;
}

题解 洛谷P3622/BZOJ1151【[APIO2007]动物园】的更多相关文章

  1. 洛谷2375 BZOJ 3670动物园题解

    题目链接 洛谷链接 我们发现题目要我们求的num[i]东西本质上其实是 求有多少以i结尾的非前缀且能与前缀匹配的字符串,而且要求字符串长度小于(i/2) 我们先不考虑字符串长度的限制,看所有以i结尾的 ...

  2. 题解 洛谷P5018【对称二叉树】(noip2018T4)

    \(noip2018\) \(T4\)题解 其实呢,我是觉得这题比\(T3\)水到不知道哪里去了 毕竟我比较菜,不大会\(dp\) 好了开始讲正事 这题其实考察的其实就是选手对D(大)F(法)S(师) ...

  3. 题解 洛谷 P3396 【哈希冲突】(根号分治)

    根号分治 前言 本题是一道讲解根号分治思想的论文题(然鹅我并没有找到论文),正 如论文中所说,根号算法--不仅是分块,根号分治利用的思想和分块像 似却又不同,某一篇洛谷日报中说过,分块算法实质上是一种 ...

  4. 题解-洛谷P5410 【模板】扩展 KMP(Z 函数)

    题面 洛谷P5410 [模板]扩展 KMP(Z 函数) 给定两个字符串 \(a,b\),要求出两个数组:\(b\) 的 \(z\) 函数数组 \(z\).\(b\) 与 \(a\) 的每一个后缀的 L ...

  5. 题解-洛谷P4229 某位歌姬的故事

    题面 洛谷P4229 某位歌姬的故事 \(T\) 组测试数据.有 \(n\) 个音节,每个音节 \(h_i\in[1,A]\),还有 \(m\) 个限制 \((l_i,r_i,g_i)\) 表示 \( ...

  6. 题解-洛谷P4724 【模板】三维凸包

    洛谷P4724 [模板]三维凸包 给出空间中 \(n\) 个点 \(p_i\),求凸包表面积. 数据范围:\(1\le n\le 2000\). 这篇题解因为是世界上最逊的人写的,所以也会有求凸包体积 ...

  7. 题解-洛谷P4859 已经没有什么好害怕的了

    洛谷P4859 已经没有什么好害怕的了 给定 \(n\) 和 \(k\),\(n\) 个糖果能量 \(a_i\) 和 \(n\) 个药片能量 \(b_i\),每个 \(a_i\) 和 \(b_i\) ...

  8. 题解-洛谷P5217 贫穷

    洛谷P5217 贫穷 给定长度为 \(n\) 的初始文本 \(s\),有 \(m\) 个如下操作: \(\texttt{I x c}\),在第 \(x\) 个字母后面插入一个 \(c\). \(\te ...

  9. 题解 洛谷 P2010 【回文日期】

    By:Soroak 洛谷博客 知识点:模拟+暴力枚举 思路:题目中有提到闰年然后很多人就认为,闰年是需要判断的其实,含有2月29号的回文串,前四位是一个闰年那么我们就可以直接进行暴力枚举 一些小细节: ...

随机推荐

  1. 土法搞docker系列之自制docker的graph driver vdisk

    写在最前 偶然整理,翻出来14年刚开始学docker的时候的好多资料.当时docker刚刚进入国内,还有很多的问题.当时我们的思考方式很简单,docker确实是个好的工具,虽然还不成熟.但是不能因为短 ...

  2. Codeforces Round #355 (Div. 2) B. Vanya and Food Processor

    菜菜菜!!!这么撒比的模拟题,听厂长在一边比比比了半天,自己想一想,然后纯模拟一下,中间过程检测一下,妥妥的就可以过. 题意:有N个东西要去搞碎,每个东西有一个高度,然后有一台机器支持里面可以达到的最 ...

  3. bzoj 2959: 长跑【LCT+并查集】

    如果没有环的话直接LCT 考虑有环怎么办,如果是静态的话就tarjan了,但是这里要动态的缩环 具体是link操作的时候看一下是否成环(两点已联通),成环的话就用并查集把这条链缩到一个点,把权值加给祖 ...

  4. iperf工具学习记录

    源码下载地址:http://sourceforge.net/projects/iperf/ 编译命令: tar -zxvf iperf-2.0.5.tar.gz cd iperf-2.0.5 ./co ...

  5. python 列表 元组

    1.列表的基本声明和赋值 #声明一个空列表 变量 = [] 或者 变量 = list() var = [1] var2 = list() # 声明带有数据的列表 var = [1,2,3,'一','二 ...

  6. 求导四则运算以及三角函数求导 Derivative formulas

    对特定函数的求导. 1:sin(x) 对其进行求斜率.带入公式得:[ sin(x+Δx)- sin(x)]/Δx  = [ sinx*cosΔx + cosx*sinΔx -sin x ]/ Δx = ...

  7. ubuntu 16.0.4安装MySQL5.7

    系统版本 root@sishen:~# uname -a Linux sishen 4.10.0-35-generic #39~16.04.1-Ubuntu SMP Wed Sep 13 09:02: ...

  8. servlet service的描述:

    链接:https://www.nowcoder.com/questionTerminal/3f368578e43d48b1968d79b935d00972来源:牛客网 1.     service方法 ...

  9. oracle 函数、聚焦函数

    oracle 常用的函数 以及 聚焦函数 --1,字符函数 --当没有表可以用个的时候oracle自带一个虚表dual -- || 表示连接符号 将字符串连接到一起 式显示 Lower(char):将 ...

  10. 关于Control.Dispatcher.BeginInvoke卡界面

    Control.Dispatcher.BeginInvoke里的逻辑由UI线程执行,如果内部包含耗时操作就会造成界面卡住. Action.BeginInvoke里的逻辑,将在一个新开的线程中执行,而不 ...