题目链接:点击打开链接
题意:
给你一个n*n的格子的棋盘,每个格子里面有一个非负数;
从中取出若干个数,使得任意的两个数所在的格子没有公共边;
就是说所取的数所在的2个格子不能相邻,并且取出的数的和最大;
理解:
状态压缩dp;
也可以用网络流做,但是不会;
递推式含义:dp[i][j] 表示第 i 行为 j 状态时的最大值;
递推式:dp[i][v[j]] = max(dp[i][v[j]], dp[i - 1][v[k]] + sum(i, v[k]));
其中 sum(i, k) 表示求第 i 行的 k 状态的值;
v[j]、v[k] 都需满足条件;
即:两两不能冲突,不能上下相邻;
即:(v[j] & v[k]) == 0;
其中 v[i] 的值表示在该值得状态中相邻为不能为 1;
即:(v[i] & (v[i] << 1)) == 0;
代码如下:
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <ctime>
#include <iostream>
#include <algorithm>
#include <vector>
#include <string>
#include <map>
#include <set>
#include <queue>
#include <stack>
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
const int MIN_INF = 1e-7;
const int MAX_INF = (1e9) + 7;
#define X first
#define Y second
LL dp[2][(1 << 20) + 10];
int w[22][22], v[(1 << 20) + 10];
LL sum(int i, int j) {
LL s = 0;
for (int a = 0; a < 21; ++a) {
if ((1 << a) & j) {
s += w[i][a];
}
}
return s;
}
int main() {
int n;
while (cin >> n) {
memset(dp, 0, sizeof(dp));
memset(v, 0, sizeof(v));
for (int i = 0; i < n; ++i) {
for (int j = 0; j < n; ++j) {
cin >> w[i][j];
}
}
int m = 0;
for (int i = 1; i < (1 << n); ++i) {
if ((i & (i << 1)) == 0) {
v[m++] = i;
dp[0][i] = sum(0, i);
}
}
int t = 0;
for (int i = 1; i < n; ++i) {
for (int j = 0; j < m; ++j) {
for (int k = 0; k < m; ++k) {
if ((v[k] & v[j]) == 0) {
dp[(t + 1) % 2][v[j]] = max(dp[(t + 1) % 2][v[j]], dp[t][v[k]] + sum(i, v[j]));
}
}
}
t = (t + 1) % 2;
}
LL ans = 0;
for (int i = 0; i < m; ++i) {
ans = max(ans, dp[t][v[i]]);
}
cout << ans << endl;
}
return 0;
}