状态压缩dp通常是一个整数就代表一个状态,而所谓状态在不同的问题中各有不同。
例一:
291. 蒙德里安的梦想
传送门
dp有状态表示,状态计算。状态表示,表示一个集合,其本身又有自己的属性。
具体以这题来看。
其状态表示为dp[i][j],其集合意义为:当第i-1列已经填充完毕之后,从第i-1列伸出来到第i列的状态,而这个状态表示为j。(j怎么理解为一个状态,将j转化为二进制表示,每一位代表一行,而当第i位为一的时候即为第i-1行有一个木块突出到第i行。
未优化代码,已优化代码。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 12, M = 1 << N;
ll dp[N][M];
bool book[M];
int main() {
int n, m;
while (cin >> n >> m, n || m) {
for (int i = 0; i < 1 << n; i++) {
book[i] = 1;
int cnt = 0;
for (int j = 0; j < n; j++) {
if (i >> j & 1) {
if (cnt & 1)
book[i] = 0;
cnt = 0;
} else cnt++;
}
if (cnt & 1)book[i] = 0;
}
memset(dp, 0, sizeof(dp));
dp[0][0] = 1;
for (int i = 1; i <= m; i++) {
for (int j = 0; j < 1 << n; j++) {
for (int k = 0; k < 1 << n; k++) {
if ((j & k) == 0 && book[j | k]) {
dp[i][j] += dp[i - 1][k];
}
}
}
}
cout << dp[m][0] << endl;
}
return 0;
}
删去无效的组合之后:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 12, M = 1 << N;
int n, m;
ll dp[N][M];
bool book[M];
vector<int>status[M];
int main() {
while (cin >> n >> m, n || m) {
for (int i = 0; i < 1 << n; i++) {
bool is_valid = true;
int cnt = 0;
for (int j = 0; j < n; j++) {
if (i >> j & 1) {
if (cnt & 1) {
is_valid = false;
break;
}
cnt = 0;
} else cnt++;
}
if (cnt & 1)is_valid = false;
book[i] = is_valid;
}
for (int i = 0; i < 1 << n; i++) {
status[i].clear();
for (int j = 0; j < 1 << n; j++) {
if ((i & j) == 0 && book[i | j]) {
status[i].push_back(j);
}
}
}
memset(dp,0,sizeof (dp));
dp[0][0]=1;
for (int i = 1; i <= m; i++) {
for (int j = 0; j < 1 << n; j++) {
for (auto it : status[j]) {
dp[i][j] += dp[i - 1][it];
}
}
}
cout << dp[m][0] << endl;
}
return 0;
}
例二:最短Hamilton路径
#include<bits/stdc++.h>
using namespace std;
const int N = 21, M = 1 << N;
int dp[M][N];
int mp[N][N];
int main() {
int n;
cin >> n;
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
scanf("%d", &mp[i][j]);
}
}
memset(dp,0x3f,sizeof (dp));
dp[1][0] = 0;
for (int i = 0; i < 1 << n; i++) {
for (int j = 0; j < n; j++) {
if (i >> j & 1) {
for (int k = 0; k < n; k++) {
if ((i - (1 << j)) >> k & 1) {
dp[i][j] = min(dp[i][j], dp[i - (1 << j)][k] + mp[k][j]);
}
}
}
}
}
cout<<dp[(1<<n)-1][n-1];
return 0;
}