2023.3.27
整理了一点状压。
拜托,但是我的状压真的学的和个什么东西一样啊。
我感觉状压 dp 真的很抽象,总结了一下状压 dp 的一些解题要点,以及下面的例题。
1.什么样的题像状态压缩 dp? 一般 n <= 20,处理问题中存在抉择过程,且每种选择都互有影响的问题。
正经来讲就是爆搜,状压也是爆搜的思路,把每一种可能的状态都枚举出来了。这样的题目,用爆搜注意剪枝,疯狂剪就能过。用状压就注意转移的条件。
2.重点:状态转移。注意什么变量的选择对答案有影响?一般用 f[i][j] 表示状态 i 时上一次从 j 转移而来。
3.难点:注意状态转移的条件:怎么才能转移?究竟是包含于当前状态还是不包含?如果有相交还能进行吗?想清楚。
4.细节问题:怎么转移过来,以及最后输出的是什么样的一种问题。
5.关键的小点:建议先写爆搜,弄清楚自己在干啥,把状态设置好,别把自己绕晕了捏!
1.AcWing:91. 最短Hamilton路径
#include<bits/stdc++.h>
using namespace std;
const int N = 21;
int n;
int a[N][N], f[1 << N][N];
int main()
{
scanf("%d", &n);
for(int i = 0; i < n; i ++ )
for(int j = 0; j < n; j ++ )
{
scanf("%d", &a[i][j]);
a[j][i] = a[i][j];
}
memset(f, 0x3f, sizeof f);
f[1][0] = 0;
for(int i = 2; i < (1 << n); i ++ )
{
for(int cur = 1; cur < n; cur ++ )
{
if(! (i & (1 << cur))) continue;
for(int pre = 0; pre < n; pre ++ )
{
if(! (i & (1 << pre)) || pre == cur) continue;
f[i][cur] = min(f[i][cur], f[i - (1 << cur)][pre] + a[pre][cur]);
}
}
}
printf("%d", f[(1 << n) - 1][n - 1]);
}
用 f[i][j] 两维状态,表示当状态为 i 时上一次选择的第 j 个城市。枚举每一种可能的状态,然后枚举最后的位置,以及上一次是从哪里转移过来的,如果满足条件可以转移(这个城市存在于本次状态),更新。最后答案 f[(1 << n) - 1][n - 1],表示每个城市都走过,从最后一个城市来。
2.AcWing:291. 蒙德里安的梦想
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 12, M = 1 << N;
int n, m;
ll f[N][M];
int st[M];
int main()
{
while(cin >> n >> m, n || m)
{
for(int i = 0; i < 1 << n; i ++ )
{
int cnt = 0;
st[i] = 1;
for(int j = 0; j < n; j ++ )
{
if(i >> j & 1 && cnt & 1) st[i] = 0;
if(!(i >> j & 1)) cnt ++;
}
if(cnt & 1) st[i] = 0;
}
memset(f, 0, sizeof f);
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 && st[j | k]) f[i][j] += f[i - 1][k];
printf("%lld\n", f[m][0]);
}
return 0;
}
需要考虑的问题:
1.将所有横向长方形放完后,竖向长方形的放置方法有且只有一种,所以只用枚举放置横向长方形的可能性。
2.满足条件1:摆放完横向长方形后,剩余位置每一行的连续方块个数不能为奇数个(若为奇数个则放竖向长方形会放不开)。实现方法:预处理每一种状态,是否合法。
3.满足条件2:在转移放置的横向长方形时,相邻的两列,横向长方形不能有冲突(比如出现一个方格构成左边一个横向长方形右边一个横向长方形),且此时两种状态相交,总状态合法。
4.最后枚举答案,按照 1 的思路,关注一下这一列和上一列的情况,统计可能放置横向长方形的方案数之和即为总答案。
5.答案 f[m][0] 意为,从 m - 1 行伸展出来,最后一列 m 的状态为 0 的个数。