状压DP
引入 :
HDU1400 -
要求使用
横着摆放且向下一列伸出
为 1,竖着摆放或由前一列伸出
为 0,
即:
则对于
只需要考虑横着的情况即可,即整个矩形的状态已经确定了,最终求
即可。
转移方程:
分别枚举第
: 若第 列和 列是不重叠的(也就是说, 列是 1, 列一定为 0,一个 代表一个 的矩形的开始和结束),即 ,且放置后满足是可摆放的,即
时间复杂度:
void solve() {
vector<int> ok(1 << 15);
vector<vector<int>> f(15, vector<int>(1 << 15));
for (int i = 0;i < 1 << n;i++) {
ok[i] = 1;
int cnt = 0;
for (int j = 0;j < n;j++) {
if (i >> j & 1) {
if (cnt & 1) {
ok[i] = 0;break;
}
} else {
cnt++;
}
}
if (cnt & 1)ok[i] = 0;
}
f[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 && ok[j | k]) {//两列状态兼容:不出现重叠的1,不出现连续的奇数个0
f[i][j] += f[i - 1][k];
}
}
}
}
cout << f[m][0] << '\n';
}
练习 :
在
Solution:
设
则有:
若
对于每个状态都要保证是可行的
先预处理出所有可行的状态,再进行操作:
时间复杂度:
当
时,可行的状态也就 89 个,所以运算次数不超过 ,运算量可以优化到极其小。
#define int long long
int f[11][1 << 11][11* 11];
void solve() {
int n, king;cin >> n >> king;
vector<int> ok;
for (int i = 0;i < 1 << n;i++) {
if ((((i << 1) | (i >> 1)) & i) == 0) {
ok.push_back(i);
}
}
f[0][0][0] = 1;
for (int i = 1;i <= n;i++) {
for (auto j : ok) {
for (auto k : ok) {
if (j & k)continue;
if ((j << 1) & k)continue;
if (j & (k << 1))continue;
for (int s = 0;s <= king;s++) {
int cnt = __builtin_popcount(j);
if (s - cnt >= 0) {
f[i][j][s] += f[i - 1][k][s - cnt];
}
}
}
}
}
int ans = 0;
for (auto x : ok)ans += f[n][x][king];
cout << ans << '\n';
}
其他练习或遇到的题目:
date: 2024-06-12 22:17:12 时间过得真快。
计划:学习状压 DP,然后补掉 ARC 179,然后 VP CFdiv 4 ,然后补邀请赛,刷...
其他类型的 DP
SOS DP
SOS DP,全称 Sum over Subsets dynamic programming,意为子集和 DP,用来解决一些涉及子集和计算的问题。