动物园

动物园

发现分析dp的题目时不能想当然的状态然后向状态转移,那样的话真的很难搞.

这个时候就要从集合的角度分析问题。(这里感谢yxc大佬的方法了.)

题目要求我们将若干个动物移走,也就是说每个动物有移或不移的选择,那总共的方案为2^m.每一个方案都有一个高兴地小朋友的人数.

选出使得小朋友高兴的人数最多的方案.这个就是我们要求问题的本质.显然我们不能暴力枚举所有的方案,这是就要用dp用集合处理问题.

但是这时的我们仍然不知道怎么设状态.那不妨考虑一下其他的问题.先考虑给定一个方案,以及所有的小朋友的喜好,如何统计答案?

这个好像也是难题....因为每一个小朋友的喜好与厌恶的动物有多个.如果我们单纯的将小朋友的人数统计在动物身上的话显然会重复计算.

每一个小朋友对应一个长度为5的区间.且每个小朋友对应区间只有一个.那我们不妨将小朋友的人数放在区间上.这是就其实我们统计答案时:

我们要做的是将答案与唯一对应的东西进行映射,这样就能做到不重不漏了.好了,我们已经将小朋友与对应的的区间进行映射.由于动物只有

两种状态,所以我们很容易想到状压,这样我们就可以想到用一个num[i][j]表示i-i+4的状态为j是的高兴地小朋友数.

想到这就很好想到状态了.我们要枚举每一个动物,所以第一维一定是前i个动物,之后由于要统计小朋友的人数,需要长度为5的区间状态,所以第二维就是i-i+4的状态.这样状态就OK了.

之后关于环形的细节,就仔细思考就行了.

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=1e4+10;
int n,c,f[N][35],num[N][35],ans; 
inline int read()
{
    int x=0,ff=1;
    char ch=getchar();
    while(!isdigit(ch)) {if(ch=='-') ff=-1;ch=getchar();}
    while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
    return x*ff;
}
int main()
{
    //freopen("1.in","r",stdin);
    n=read();c=read();
    for(int i=1;i<=c;++i)
    {
        int e=read(),f=read(),l=read();
        int like=0,dislike=0;
        for(int j=1;j<=f;++j)
        {
            int x=read();
            int t=x-e;
            if(t<0) t+=n;
            dislike|=(1<<t);
        }
        for(int j=1;j<=l;++j)
        {
            int x=read();
            int t=x-e;
            if(t<0) t+=n;
            like|=(1<<t);
        }
        for(int j=0;j<32;++j)
            if(((j&dislike)!=dislike)||(like&j)) num[e][j]++;
    }
    for(int base=0;base<32;++base)
    {
        memset(f,0xef,sizeof(f));
        f[1][base]=num[1][base];
        for(int i=2;i<=n;++i) 
            for(int j=0;j<32;++j)
                f[i][j]=max(f[i-1][(j&15)<<1],f[i-1][(j&15)<<1|1])+num[i][j];
        ans=max(ans,max(f[n][(base&15)<<1],f[n][(base&15)<<1|1]));        
    }
    printf("%d",ans);
    return 0;
}
View Code

 

posted @ 2020-03-30 11:02  逆天峰  阅读(175)  评论(0编辑  收藏  举报
作者:逆天峰
出处:https://www.cnblogs.com/gcfer//