这道题非常爽魔,我一个一点没有接触过状压dp的蒟蒻,在DFS题单里刷到了它,花了两个多小时理解题解的每一行代码,代码的结构实在是太全新了!让我受益匪浅
#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;
// 每个格子,要么选,要么不选,那么对于这个N*M矩阵,产生的状态数组合就有2^(n*m)
// 可以达到2^36这么多,显然时间复杂度直接爆炸了,为此我们必须得进行状态压缩
// 于是就能想到使用状态dp算法
int main(){
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
int T;
cin >> T;
while (T--) {
int n, m;
cin >> n >> m;
vector<vector<long long>> a(n, vector<long long>(m));
for (int i = 0; i < n; i++) { // 得到整个矩阵
for (int j = 0; j < m; j++) {
cin >> a[i][j];
}
}
// 接下来对整个矩阵进行状态压缩将状态压缩到一行里,合法的不相邻选择列组合是什么
vector<long long> state;
for (int i = 0; i < (1<<m) ; i++) { // m列,则有2^n的列组合,如001,101,111
if ((i & i << 1) == 0) {
// 101 和 010,相当于让001组合相邻元素进行比较,看看是否满足相邻元素不为1
state.push_back(i); // 将这一101组合压入state,表示一个合法的列组合
}
}
// 得到行内部的合法列组合,接下来就把每一行的和合法列方案选取到的矩阵元素相加,
// 得到在当前行,不同组合010,101他们的行权值和是多少
long long s = state.size();
vector<vector<long long>> rol_sum(n, vector<long long>(s));
// 利用rol_sum记录不同方案下这一行的对应权值和
for (int i = 0; i < n; i++) { // 每一行
for (int z = 0; z < s; z++) { // 每一种列方案
for (int j = 0; j < m; j++) { // 该行上某列的元素值
if (state[z] & 1 << j) {
rol_sum[i][z] += a[i][j]; // 第i行第z个方案的列权值和
}
}
}
}
// 之后我们要确定行间也要合法,只需比较上一行和本行,不用管下一行,
// 因为dp算法只看当前的和上一个的
vector<vector<long long>> ok(s, vector<long long>(s, 0));
// 创建一个ok数组,记录第i个合法列组合方案和第j个合法列组合方案是否兼容
for (int curr = 0; curr < s; curr++) {
for (int pre = 0; pre < s; pre++) {
if ((state[curr] & state[pre]) == 0 && (state[curr] & state[pre] << 1) == 0 && (state[curr] & state[pre] >> 1) == 0) {
ok[curr][pre] = 1; // 1表示上行的列组合和本行的列组合兼容
}
}
}
long long DEG = -2e18;
vector<vector<long long>> dp(n, vector<long long>(s, DEG));
// 因为要找最大数字和,那我得让dp被初始化为极其小的数,然后dp表示在第i行,当使用第k个列组合方案时,以该行为结尾的矩阵能取得的最大合法数字和
for (int i = 0; i < s; i++) {
dp[0][i] = rol_sum[0][i]; // 第0行的dp当然是取第0行的行权值和
}
for (int i = 1; i < n; i++) {
for (int curr = 0; curr < s; curr++) {
for (int pre = 0; pre < s; pre++) {
if (ok[curr][pre]) {
dp[i][curr] = max(dp[i][curr], dp[i-1][pre] + rol_sum[i][curr]);
}
}
}
}
long long maxx = DEG;
for (int i = 0; i < s; i++) {
maxx = max(maxx, dp[n-1][i]); // dp[n-1]表示以最后一行为结尾的矩阵所能选出的最大权值和,[i]则是第i个列组合方案下的最大权值和
}
cout << maxx << '\n';
}
return 0;
}

京公网安备 11010502036488号