【APIO2007】【状态压缩】动物园

【题目描述】
新建的圆形动物园是亚太地区的骄傲。圆形动物园坐落于太平洋的一个小岛上,包含一大圈围栏,每个围栏里有一种动物。如下图所示:
在这里插入图片描述
你是动物园的公关主管。你要做的是,让每个参观动物园的游客都尽可能高兴。今天有一群小朋友来到动物园参观,你希望能让他们在动物园度过一段美好的时光。但这并不是一件容易的事——有些小朋友喜欢某些动物,而有些小朋友则害怕某些动物。例如, Alex 喜欢可爱的猴子和考拉,而害怕拥有锋利牙齿的狮子。而 Polly 会因狮子有美丽的鬃毛而喜欢它,但害怕有臭味的考拉。

你可以选择将一些动物从围栏中移走以使得小朋友不会害怕。但你移走的动物也不能太多,否则留给小朋友们观赏的动物就所剩无几了。

每个小朋友站在大围栏圈的外面,可以看到连续的 5 个围栏。你得到了所有小朋友喜欢和害怕的动物信息。当下面两处情况之一发生时,小朋友就会高兴:

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

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

例如,考虑下图中的小朋友和动物:
在这里插入图片描述
假如你将围栏 4 和 12 的动物移走。Alex 和 Ka-Shu 将很高兴,因为至少有一个他们害怕的动物被移走了。这也会使 Chaitanya 高兴,因为他喜欢的围栏 6 和 8 中的动物都保留了。但是,Polly 和 Hwan 将不高兴,因为他们看不到任何他们喜欢的动物,而他们害怕的动物都还在。这种安排方式使得三个小朋友高兴。

现在换一种方法,如果你将围栏 4 和 6 中的动物移走,Alex 和 Polly 将很高兴,因为他们害怕的动物被移走了。Chaitanya 也会高兴,虽然他喜欢的动物 6 被移走了,他仍可以看到围栏 8 里面他喜欢的动物。同样的 Hwan 也会因可以看到自己喜欢的动物 12 而高兴。唯一不高兴的只有 Ka-Shu。

如果你只移走围栏 13 中的动物,Ka-Shu 将高兴,因为有一个他害怕的动物被移走了,Alex, Polly, Chaitanya 和 Hwan 也会高兴,因为他们都可以看到至少一个他们喜欢的动物。所以有 5 个小朋友会高兴。这种方法使得了最多的小朋友高兴。

输入
输入的第一行包含两个整数 N,C ,用空格分隔。N 是围栏数, C 是小朋友的个数。围栏按照顺时针的方向编号为 1,2,3,…,N。

接下来的 C 行,每行描述一个小朋友的信息,以下面的形式给出:E F L X1 X2 ⋯XF Y1 Y2 ⋯YLE F L X1 X2 ⋯XF Y1 Y2 ⋯YL
其中:EEE 表示这个小朋友可以看到的第一个围栏的编号,换句话说,该小朋友可以看到的围栏为 E,E+1,E+2,E+3,E+4E,E+1,E+2,E+3,E+4。注意,如果编号超过 NN将继续从 11开始算。如:当N=14,E=13N=14,E=13时,这个小朋友可以看到的围栏为 13,14,1,2 和 3。

F 表示该小朋友害怕的动物数。L 表示该小朋友喜欢的动物数。

围栏X1,X2,⋯,XFX1,X2,⋯,XF 中包含该小朋友害怕的动物。围栏 Y1,Y2,⋯,YLY1,Y2,⋯,YL 中包含该小朋友喜欢的动物。

X1,X2,⋯,XF,Y1,Y2,⋯,YLX1,X2,⋯,XF,Y1,Y2,⋯,YL是两两不同的整数,而且所表示的围栏都是该小朋友可以看到的。

小朋友已经按照他们可以看到的第一个围栏的编号从小到大的顺序排好了(这样最小的 E 对应的小朋友排在第一个,最大的 E 对应的小朋友排在最后一个)。

注意可能有多于一个小朋友对应的 E 是相同的。

【输出】
仅输出一个数,表示最多可以让多少个小朋友高兴

【样例】
样例输入 1
14 5
2 1 2 4 2 6
3 1 1 6 4
6 1 2 9 6 8
8 1 1 9 12
12 3 0 12 13 2
样例输出 1
5
样例说明 1
样例 1 给出了前面描述的示例情形。它使得所有小朋友(C=5 )高兴。

样例输入 2
12 7
1 1 1 1 5
5 1 1 5 7
5 0 3 5 7 9
7 1 1 7 9
9 1 1 9 11
9 3 0 9 11 1
11 1 1 11 1
样例输出 2
6
样例说明 2
样例 2 给出了这样的情形,要使得所有小朋友(C=7 )都高兴是不可能的。
对于全部数据,10≤N≤104,1≤C≤5×104,1≤E≤N

【思路】

这道题由于小朋友可以看见的区域只有5,所以我们可以考虑状态压缩。用1表示移走的动物,用0表示没有移走的动物。于是我们就可以用f[i][j]表示当前位置为i,前五个位置状态为j的满意的小朋友为f[i][j]。那么我们可以预处理出每个数的前驱状态,从而进行更新。小朋友的状态可以保存在vector里面,每个状态更新以后取小朋友出来看能否使当前状态答案加1。那么我们还剩一个问题需要解决,即对于环的处理。如果我们直接从第5个位置更新到第n+4个位置,我们会发现1-4的动物被选择了两次,导致答案可能偏大。因此我们考虑每次枚举前五个的状态s,从该状态s出发去更新后面的状态,那么最后合法的可以用来更新答案的状态就只有dp[n+4][pre(s)]。
代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<vector>
#include<queue>
#define mp() make_pair()
#define re register
using namespace std;
int n,m,a,b,k,num1,num2;
typedef pair<int,int>T;
vector<T>q[10005];
int now1=0,now2=0;
int f[10008][32];
inline int pre1(int j)
{
	j%=16;
	j<<=1;
	return j;
}
inline int pre2(int j)
{
	j%=16;
	j<<=1;
	return j+1; 
}
int c1[32];
int c2[32];
int main()
{
	for(int re i=0;i<=31;i++)
	{
		c1[i]=pre1(i);
		c2[i]=pre2(i);
	}
	scanf("%d%d",&n,&m);
	while(m--)
	{
		scanf("%d%d%d",&a,&num1,&num2);
		now1=0,now2=0;
		while(num1--)
		{
			scanf("%d",&b);
			if(b<a)b+=n;
			now1=now1|(1<<(b-a));	
		}
		while(num2--)
		{
			scanf("%d",&b);
			if(b<a)b+=n;
			now2=now2|(1<<(b-a));
		}
		q[a+4].push_back(make_pair(now1,now2));
	}
	int tot=31;
	int ans=0;
	for(int re p=0;p<=tot;p++)
	{
		memset(f,-30,sizeof(f));f[5][p]=0;
		for(int re k=0;k<q[5].size();k++)
		{
				T s=q[5][k];
				if((p&s.first)||(~p&s.second))f[5][p]++;
		}
		for(int re i=6;i<=n+4;i++)
		{
			for(int re j=0;j<=tot;j++)
			{
				f[i][j]=max(f[i-1][c1[j]],f[i-1][c2[j]]);
				for(int re k=0;k<q[i].size();k++)
				{
					T s=q[i][k];
					if((j&s.first)||((j^s.second)&s.second))f[i][j]++;
				}
			}
		}
		ans=max(ans,f[n+4][c1[p]]);
		ans=max(ans,f[n+4][c2[p]]);
	}

	cout<<ans;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值