状压学习笔记

  状态压缩利用二进制枚举每一种状态,用0和1表示取或不取,然后通过位运算实现。例如,现在有20个物品,分别用一个数在二进制下20位表示取或不取,例如,10在二进制下位1010,即代表选取第2和4个物品。对一个数x,判断第i个物品是否被选取可以用位运算(1<<i)&x判断。状态压缩通常伴随dp一起出现,是一种时间复杂度很高很暴力的算法。

  经典例题:hdu1565方格取数

  http://acm.hdu.edu.cn/showproblem.php?pid=1565

  给一个n*n的方阵,取若干个数,要求任意两个数所在方格不相邻,求取出数的最大和。

  首先预处理出每行的取数方案,对于i,用1代表取,0代表不取,若(i>>1)&i为0,即代表i方案可以被选用,因为相邻两项不存在同为1的。

  然后对于每行,枚举该行选取方案,对上一行枚举方案枚举,若两方案与值为0则可以转移,因为此时没有同一列的被选取。自上而下转移即可。

  贴上AC代码。

  

#include<bits/stdc++.h>

using namespace std;
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
typedef pair <int,int> pii;
#define rep(i,x,y) for(int i=x;i<y;i++)
#define rept(i,x,y) for(int i=x;i<=y;i++)
#define per(i,x,y) for(int i=x;i>=y;i--)
#define pb push_back
#define mp make_pair
#define fi first
#define se second
#define de(x) cout<< #x<<" = "<<x<<endl
#define dd(x) cout<< #x<<" = "<<x<<" "
#define mes(a,b) memset(a,b,sizeof a)
const int inf= 0x3f3f3f3f;

int way[1<<15],cnt=0;
int matrix[25][25];

int dp[25][1<<15];
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    int n;
    for(int i=0;i<(1<<20);i++)
        if((i&(i>>1))==0)
            way[cnt++]=i;
//    de(cnt);
//    de((1<<15));
    while(cin>>n)
    {
        mes(dp,0);
        rept(i,1,n)
            rept(j,1,n)
                cin>>matrix[i][j];
        rept(i,1,n)
        {
            rep(j,0,cnt)
            {
                if((1<<n)<=way[j]) break;
                int sum=0,choose=way[j],p=1;
                while(choose)
                {
                    if(choose&1) sum+=matrix[i][p];
                    p++;
                    choose>>=1;
                }
            //    dd(i);dd(j);de(sum);
                dp[i][j]=0;
                rep(k,0,cnt)
                {
                    if((1<<n)<=way[k]) break;
                    if((way[j]&way[k])==0)
                    {
//                        dd(j);de(k);
                        dp[i][j]=max(dp[i][j],dp[i-1][k]+sum);
                    }
                }
            }
        }
        int ans=0;
        rep(i,0,cnt)
        {
    //        dd(i);
            if((1<<n)<=way[i]) break;
            ans=max(ans,dp[n][i]);
        }
        cout<<ans<<"
";
    }
    return 0;
}
原文地址:https://www.cnblogs.com/FZUzyz/p/11910855.html