zoukankan      html  css  js  c++  java
  • Luogu [P3622] [APIO2007]动物园

    题目链接

    比较费脑子的一道题

    先说题目核心思想 : 状压dp

    环的处理我们先不管。

    我们设 dp[j][s] 表示 到达动物 j 且 [ j , j+5) 这五个动物状态为s时 最多能使多少小朋友开心。

    其中,s为 0~31 的整数,二进制下的s表示[ j , j+5) 这五个动物状态,0表示不选,1表示选,特别注意,[ j , j+5) 分别对应s从右往左数的每个数字,例如s = 18 ,二进制下表示为10010 ,即 j 到 j+4 的状态为 0 , 1 , 0 , 0 , 1。

    可得状态转移方程:dp[j][s] = min( dp[j-1][(s&15)<<1] , dp[j-1][(s&15)<<1|1] ) + num[j][s];

    解释:15 的二进制为01111,(s&15) 即取 [ j , j+5) 的前 4 个数的状态。然后 <<1 代表作为 [j-1 , j+4)的后四个状态,对j-1的状态枚举一下是0还是1,取个min,再加上num[j][s]就行了

    num[j][s]为已经预处理出来的数组,它的意思为:

    状态为s时,某几个视野为[j , j+5)的小朋友中高兴地人数

    还是有点懵?没事,将上面的解释结合下图来看:

    当前 j=3 , s=18 (10010) , s&5=2 (0010)
    动物:             1    2    3    4    5    6    7    8    9    10
    下标:                 j-1   j   j+1  j+2  j+3  j+4
    s状态:                      0    1    0    0    1
    (s&5)为:                    0    1    0    0
    (s&5)>>1状态:          0    0    1    0    0
    (s&5)>>1|1状态:        1    0    1    0    0

    这样就比较好理解了。

    num的预处理

    可随读入一起处理:

    for(int i=1;i<=m;i++){
            int E=read(),F=read(),L=read(),fear=0,like=0;
            for(int j=1;j<=F;j++){
                int x=read() ;
                x=(x-E+n)%n;
                fear|=(1<<x);//fear表示这五个围栏中这个小朋友害怕的动物的状态 
            }
            for(int j=1;j<=L;j++){
                int x=read();
                x=(x-E+n)%n;
                like|=(1<<x);//like表示这五个围栏中这个小朋友喜欢的动物的状态 
            }
            for(int j=0;j<32;j++)//0-> 拿走 1->留下 
                if((fear&~j)||(like&j))
                    num[E][j]++;
        }

    较难理解的几点:1.x = (x-E+n)%n,fear |= (1<<x)

    这个为环的处理,手推几个式子就能得出

    2.(fear&~j)||(like&j)

    看题目条件:

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

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

    这里留给读者自行思考,不再解释( '~'符号为取反,即0变1,1变0

    然后就是最后dp环的处理:

    可以枚举开始的状态 ,设开始0的状态为 i 则最后的状态只有 dp[n][i] 对答案有效,强制和开始的状态一样

    OK结束,代码:

    #include <algorithm>
    #include <iostream>
    #include <cstring>
    #include <cstdio>
    #include <cmath>
    using namespace std;
    int n,m,ans;
    int num[50005][35];
    int dp[10005][35];
    inline int read(){
        int x=0;
        char ch=getchar();
        while(ch<'0'||ch>'9')
            ch=getchar();
        while(ch>='0'&&ch<='9'){
            x=(x<<1)+(x<<3)+(ch^48);
            ch=getchar();
        }
        return x;
    }
    int main(){
        n=read(),m=read();
        for(int i=1;i<=m;i++){
            int E=read(),F=read(),L=read(),fear=0,like=0;
            for(int j=1;j<=F;j++){
                int x=read() ;
                x=(x-E+n)%n;
                fear|=(1<<x);
            }
            for(int j=1;j<=L;j++){
                int x=read();
                x=(x-E+n)%n;
                like|=(1<<x);
            }
            for(int j=0;j<32;j++)//0-> 拿走 1->留下 
                if((fear&~j)||(like&j))
                    num[E][j]++;
        }
        for(int i=1;i<=n;i++){
            for(int j=0;j<32;j++){
                cout<<num[i][j]<<" ";
            }
            cout<<endl;
        }
        for(int i=0;i<32;i++){
            memset(dp[0],128,sizeof(dp[0]));
            dp[0][i]=0;
            for(int j=1;j<=n;j++)
              for(int s=0;s<32;s++)
                dp[j][s]=max(dp[j-1][(s&15)<<1],dp[j-1][(s&15)<<1|1])+num[j][s];
            if(ans<dp[n][i])//开始状态为 i 结尾状态也得是i才能更新ans 
              ans=dp[n][i];
        }
        cout<<ans;
        return 0;
    }
  • 相关阅读:
    只因写了一段爬虫,公司200多人被抓!
    中国顶级程序员图鉴
    漫画 | 外行对程序员误会有多深!
    关于深夜技术事故纪实录的若干问题回复
    富士康14跳被我赶上了,流水线车间真的没有梦想 | 十年系列
    祖国和我们小山村的希望
    互联网从此没有 BAT
    程序员,职场上请远离这种人!
    HTML5漂亮实用的电子书
    这个jQuery导航菜单怎么样
  • 原文地址:https://www.cnblogs.com/qiuchengrui/p/11719853.html
Copyright © 2011-2022 走看看