caioj1421&&hdu2167: [视频]【状态压缩】选数

%hz大佬。。这道题的状态压缩简直匪夷所思(其实是我孤陋寡闻,而且我以前的博客竟然写了这题。。水啊)

嗯这题可以发现,我们可以用一个二进制表示一行的状态,1表示选0反之,可以发现行与行之间可选的范围是确定的,比如说:
100
这样的状态适用于每一行,推广一下:
100
001
是适用于任何两行之间的。所以我们可以先将所有成立的状态求出来,要求就是左移一位和右移一位和原状态&运算为0,这样保证每个1左右都没有1。然后枚举行、状态,可以继承的状态,继承可以继承的状态的最大值,然后加上这一行的得到的值即可。

#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
int n,mp[20][20];
int ol,o[41000],f[20][41000];
bool bj(int x,int y)
{
    return ((x&y)==0)?true:false;
}
int hehe(int k,int x)
{
    int tp=0,ans=0;
    while(x!=0)
    {
        tp++;
        if(x%2==1)ans+=mp[k][tp];
        x/=2;if(tp==n)break;
    }
    return ans;
}
int main()
{
    scanf("%d",&n);
    int t=1;for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=n;j++)
            scanf("%d",&mp[i][j]);
        t=t*2;
    }
    o[++ol]=0;f[1][0]=0;
    for(int i=1;i<t;i++)
        if(bj(i,i/2)==true&&bj(i,i*2)==true)
        {
            o[++ol]=i;
            f[1][i]=hehe(1,i);
        }
    
    for(int k=2;k<=n;k++)
    {
        for(int i=1;i<=ol;i++)
        {
            for(int j=1;j<=ol;j++)
            {
                if(bj(o[i],o[j])==true&&bj(o[i],o[j]/2)==true&&bj(o[i],o[j]*2)==true)
                {
                    f[k][o[i]]=max(f[k][o[i]],f[k-1][o[j]]);
                }
            }
            f[k][o[i]]+=hehe(k,o[i]);
        }
    }
    int ans=0;
    for(int i=1;i<=ol;i++)
        if(f[n][o[i]]>ans)ans=f[n][o[i]];
    printf("%d
",ans);
    return 0;
}
原文地址:https://www.cnblogs.com/AKCqhzdy/p/7612866.html