洛谷3622 bzoj1151 APIO2007 CTSC2007 动物园 状压dp

题目链接
题意还是自己看题吧,比较麻烦,一两句话说不清楚。

题解:
这个题是道好题。
乍一看这数据范围根本不像状压,仔细想想才会发现其实每个人只能看到5个位置,所以我们可以对5状压。
可能会有人看到的区域相同,所以我们设 c n t [ i ] [ j ] 表示在第 i 个位置,从该位置开始,连续五个位置的状态是 j ,有多少个视野初始位置在 i 的人会开心。其中对于某个二进制位,0表示该位置没有动物了,1表示该位置还有动物。
c n t [ i ] [ j ] 的描述有点绕,可能是我语文不太好。。。
c n t [ i ] [ j ] 的计算方法是把每个人喜欢和害怕的动物也状压一下,然后枚举 2 5 (32)种状态,通过位运算判断当前枚举到的状态是否能使这个人高兴。
然后我们要做的是这个环的dp,但是我们的状态却是从该位置开始连续5位的状态,所以会出现第1个位置的前5位与看成链之后的从最后一位开始的5位状态不同,那么我们要枚举这5位的所有状态,然后再从1到n进行dp。我们设 d p [ i ] [ j ] 表示第 i 个位置,从第 i 个位置开始连续5个位置的状态是 j 最多有多少人高兴。状态转移方程应该是dp[i][j]=max(f[i][(j&15)<<1],dp[i][(j&15)<<1|1]+num[i][j],其中(j&15)是取出来当前状态的后4位,而当前状态的后4位正是第 i 1 位时状态的前4位,所以枚举 i 1 位时状态的最后一位是0还是1答案更大,取max之后再加上当前位置的贡献。
代码中最终更新ans时只用 d p [ n ] [ i ] ,因为我们是枚举第一位的前5位的状态是 i 的情况,所以只有 d p [ n ] [ i ] 是合法的,另外初始化时除了 d p [ 0 ] [ i ] 之外的情况都赋为无穷小,只有 d p [ 0 ] [ i ] 赋为0,让其他初始状态不会对当前枚举的初始状态的答案产生影响。

PS:上面的dp方程没用markdown,因为不会用markdown打&符号。。。(有人愿意教我markdown怎么打&符号吗)

代码:

#include <bits/stdc++.h>
using namespace std;

int n,m,dp[50010][35],cnt[50010][35],ans;
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;++i)
    {
        int x,y,z;
        scanf("%d%d%d",&x,&y,&z);
        int l=0,f=0;
        for(int j=1;j<=y;++j)
        {
            int q;
            scanf("%d",&q);
            q=(q-x+n)%n;
            f|=(1<<q);
        }
        for(int j=1;j<=z;++j)
        {
            int q;
            scanf("%d",&q);
            q=(q-x+n)%n;
            l|=(1<<q);
        }
        for(int i=0;i<32;++i)
        {
            if((i&l)||((~i)&f))
            ++cnt[x][i];
        }
    }
    for(int i=0;i<32;++i)
    {
        for(int j=0;j<32;++j)
        dp[0][j]=-2e9;
        dp[0][i]=0;
        for(int j=1;j<=n;++j)
        {
            for(int k=0;k<32;++k)
            {
                dp[j][k]=max(dp[j-1][(k&15)<<1],dp[j-1][(k&15)<<1|1])+cnt[j][k];
            }   
        }
        ans=max(ans,dp[n][i]);
    }
    printf("%d\n",ans);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/forever_shi/article/details/80933617