状态压缩--方格取数

Posted 李卓伦

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了状态压缩--方格取数相关的知识,希望对你有一定的参考价值。

状态压缩--方格取数


题目:

给你一个n*n的格子的棋盘,每个格子里面有一个非负数。
从中取出若干个数,使得任意的两个数所在的格子没有公共边,就是说所取的数所在的2个格子不能相邻,并且取出的数的和最大。

input

包括多个测试实例,每个测试实例包括一个整数n 和n*n个非负数(n<=20)

output

对于每个测试实例,输出可能取得的最大的和

sample input

3
75 15 21
75 15 28
34 70 5

sample output

188

解题思路:

  • 找出每一行的状态的状态用二进制保存,0表示该位置不取,1表示取
  • 将每一行所有状态的和保存到数组里面
  • 从第二行开始找该行与上一行不冲突的所有状态,保存状态最大之和, 一直找到最后一行为止
  • 遍历最后一行所有状态数值之和,最大值即为答案

需要用到异或,感觉在状态压缩里面特别重要

(1)按位与运算符(&)

按位与运算将两个运算分量的对应位按位遵照以下规则进行计算:

0 & 0 = 0, 0 & 1 = 0, 1 & 0 = 0, 1 & 1 = 1。

即同为 1 的位,结果为 1,否则结果为 0。

例如,设3的内部表示为011
5的内部表示为101
3&5的结果为001

(2)按位或运算符(|)

按位或运算将两个运算分量的对应位按位遵照以下规则进行计算:

 0 | 0 = 0, 0 | 1 = 1, 1 | 0 = 1, 1 | 1 = 1

即只要有1个是1的位,结果为1,否则为0。

(3)按位异或运算符(^)

按位异或运算将两个运算分量的对应位按位遵照以下规则进行计算:

 0 ^ 0 = 0, 0 ^ 1 = 1, 1 ^ 0 = 1, 1 ^ 1 = 0

即相应位的值相同的,结果为 0,不相同的结果为 1。
例如,013^035结果为026
异或运算的意思是求两个运算分量相应位值是否相异,相异的为1,相同的为0。按位异或运算的典型用法是求一个位串信息的某几位信息的反。如欲求整型变量j的最右4位信息的反,用逻辑异或运算017^j,就能求得j最右4位的信息的反,即原来为1的位,结果是0,原来为0的位,结果是1。

AC代码

#include <iostream>
#include <string.h>
#define M 17720      //每一行有最大17711种状态
#define N 21
using namespace std;
int map[N][N];     //储存输入的数字
int sum[N][M];  //第i行第j种状态的总和
int state[M];
int dp[N][M];
int n,p;
int max(int a,int b)    // 求最大值
{
    return (a>b?a:b);
}
bool checkLine(int i)       //检查该状态是否符合题意
{
    return !(i&(i>>1));
}
bool checkTwoLine(int i,int j)  //检查该行与上一行是否符合题意
{
    return !(i&j);
}
void init()
{
    int i,j,k;
    p=0;
    memset(sum,0,sizeof(sum));
    for(i=0;i<(1<<n);i++)  //将n改为20测试获得每一行有17711种状态
        if(checkLine(i))
            state[p++]=i;

    for(i=0;i<n;i++)
        for(j=0;j<p;j++)
            for(k=0;k<n;k++)
                if((state[j]>>k)&1)
                sum[i][j]+=map[i][n-k-1];

}
void solve()
{

    int i,j,k;
    memset(dp,0,sizeof(dp));
    for(i=0;i<p;i++)
        dp[0][i]=sum[0][i];

    for(i=1;i<n;i++) //遍历每一行
        for(j=0;j<p;j++)   //遍历该行的每一种状态
            for(k=0;k<p;k++)   //遍历上一行的每一种状态
            {
                if(checkTwoLine(state[j],state[k]))
                    dp[i][j]=max(dp[i][j],sum[i][j]+dp[i-1][k]);
            }
    int ans=dp[n-1][0];
    for(i=1;i<p;i++)
    {
        if(ans<dp[n-1][i])
            ans=dp[n-1][i];
    }
    cout<<ans<<endl;


}
int main()
{

    while (cin>>n)
    {
        int i,j;
        memset(map,0,sizeof(map));
        for(i=0;i<n;i++)
            for(j=0;j<n;j++)
            cin>>map[i][j];
        init();

        solve();
    }
    return 0;
}

以上是关于状态压缩--方格取数的主要内容,如果未能解决你的问题,请参考以下文章

HDU 2167 状压dp方格取数

hdu 2167 方格取数 状压dp(经典)

HDU 1565 方格取数

HDU1565(状态压缩dp)

洛谷 2774方格取数问题 | 状压DP

dp算法之方格取数