动态规划-状态压缩DP

今天给大家带来的是一道状态压缩DP问题,这类动态规划问题的难度较大,需要大家好好理解一下。

题目描述

小明拥有一个 N×M 的矩阵型宠物袋 A,宠物袋的一个单元刚好可以装下一只宠物。

起初,宠物袋的某些单元已经放置了小明买的食物,这些单元将不能再用来装宠物。

现在小明准备去宠物商店购买宠物,并将购买的宠物放入宠物袋带回家。

然而商店的宠物之间的关系一直不和,一旦两宠物放在相邻(上下左右)的单元,它们就会打起来。

小明想带尽可能多的宠物回家,同时也不希望宠物之间会打架。

请你帮他算算,他最多可以带多少宠物回家。

输入描述

输入第 1 行包含两个整数 N,M,表示小明宠物袋的大小。

接下来 N行,每行包含 M 个数—— aij,表示宠物袋该单元起初是否已被用来装东西(ai,j​=1 表示该单元已经装了食物,不能用来装宠物了;ai,j​=0 表示该单元可以用来装宠物)。

1≤N≤30,1≤M≤10,ai,j​∈(0,1)。

输出描述

输出一行整数表示小明所能带走的最多宠物数。

#include 
using namespace std;

// 定义常量N和M,N用于表示矩阵的行数上限,M用于表示矩阵的列数上限
const int N = 32;
const int M = 15;
// dp[i][j]表示前i行已经处理好,且第i行状态为j(j以二进制的形式处理)所能带走的最大宠物数
// 对于状态j,二进制位为1表示该位放了宠物,为0表示该位没有放宠物
int dp[N][1<> 1; 
    }
    return cnt;
}

int main() {
    int n, m;
    // 读取宠物袋的行数n和列数m
    cin >> n >> m; 
    // 计算每行状态的上限,即m个1的二进制数,例如m = 3时,上限为111(二进制)
    int upper_limit = (1<> tmp; 
            // 如果该位置放了食物(tmp为1),则将rows[i]对应二进制位设为1
            if (tmp) rows[i] |= 1<<(j - 1); 
        }    
    } 

    // 预处理valid数组,验证每一种状态(00...000 ~ 11...111)是否合法
    for (int i = 0; i <= upper_limit; i++) { 
        // 如果i与i右移一位后的数按位与结果为0,说明i没有相邻的1,是合法状态
        if ((i & (i >> 1)) == 0) valid[i] = true; 
        else valid[i] = false; 
    }

    // 枚举每一行i
    for (int i = 1; i <= n; i++) { 
        // 枚举该行的每一种状态j
        for (int j = 0; j <= upper_limit; j++) { 
            // valid[j]为true表示状态j本身是一种合法的状态
            // rows[i] & j == 0表示第i行的状态可以是j,即放宠物的位置没有占用食物的位置
            if ((rows[i] & j) == 0 && valid[j]) { 
                // 枚举前i - 1行所有可能的状态k
                for (int k = 0; k <= upper_limit; k++) { 
                    // valid[k]为true,表示k本身是一个合法的状态
                    // j & k == 0,表示前一行与当前行的任意一列都不会出现两个宠物
                    if ((j & k) == 0 && valid[k]) { 
                        // 累加第i - 1行状态为k的所有方案再加上状态j中"1"的个数(第i行可以放宠物的位置)
                        // 取其中的最大值更新dp[i][j]
                        dp[i][j] = max(dp[i][j], dp[i - 1][k] + cnt_1(j)); 
                    }
                }
            }
        }
    }

    int ans = 0;
    // 枚举第n行所有可能的状态00...000 ~ 11...111
    for (int i = 0; i <= upper_limit; i++) { 
        // 比较得出最大值
        ans = max(ans, dp[n][i]); 
    } 
    // 输出小明所能带走的最多宠物数
    cout << ans << endl; 
    return 0;
}

我将这道题的相关注释都标在了代码中,大家可以看看好好理解。

这里说明几个易错点:

1.当用二进制表示的时候,a和b进行位运算位数,当a位数小于b时,高位自动补0进行运算。

2.用二进行初始化定义数组时,默认值为0,比方说有5位,存入2和3位为1,则就是00110.

今天的分享就到这里,希望大家能够好好理解这一部分,难度较大。

希望大家多多关注和点赞,十分感谢。

你可能感兴趣的:(动态规划,算法)