DP——状态压缩DP、树形DP
状态压缩DP
AcWing 291. 蒙德里安的梦想
- 思路:
- 状态压缩:对于每一列的摆放状态,1表示横放(会占据右边邻列同行格子),0表示不横放。
- 对于任意一列,连续0的个数一定要是奇数才满足条件
- f[i][j]表示前i列中第i列为状态j的方案集合,值为方案个数
- 状态转移条件可以简化为st[i | j] && (i&j)==0
- &&左边表示第i列自己横放的和左邻列横放的满足要求(对应上述第二点)
- 右边条件表示相邻两列的同行不能同时横放
- 初始化:
- 起点边界:f[0][0] = 1
- 非法状态:f[i][j] = 0
#include<iostream>
#include<cstring>
using namespace std;
const int N = 12;
typedef long long LL;
int n, m;
bool st[1 << N];
LL dp[N][1 << N];
int main()
{
while(cin >> n >> m, n || m)
{
for(int i = 0; i < 1 << n; i++)
{
st[i] = true;
int cnt = 0;
for(int j = 0; j < n; j++)
{
if(i >> j & 1)
{
if(cnt & 1)
{
st[i] = false;
break;
}
cnt = 0;
}
else cnt++;
}
if(cnt & 1)st[i] = false;
}
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 && st[j | k])
dp[i][j] += dp[i - 1][k];
cout << dp[m][0] << endl;
}
return 0;
}
AcWing 91. 最短Hamilton路径
- 状态压缩:路径上经过了哪几个点可以用二进制数来表示,从左到右第i位为1表示经过了第i - 1个点。
- dp[i][j]表示从0开始经过了二进制数i表示的所有点,终点为j的所有路径,值为最短路径值。
- 根据路径倒数第二个点对状态进行分类
- 初始化:
- 非法状态:f[i][j] = 0x3f3f3f3f(足够大)
- 起点边界:f[1][0] = 0
#include<iostream>
#include<cstring>
using namespace std;
const int N = 21;
int dp[1 << N][N];
int g[N][N], n;
int main()
{
cin >> n;
for(int i = 0; i < n; i++)
for(int j = 0; j < n; j++)
cin >> g[i][j];
memset(dp, 0x3f, sizeof dp);
dp[1][0] = 0;
for(int i = 1; i < 1 << n; i++)
for(int j = 0; j < n; j++)
if(i >> j & 1)
for(int k = 0; k < n; k++)
if(i >> k & 1)
dp[i][j] = min(dp[i][j], dp[i - (1 << j)][k] + g[k][j]);
cout << dp[(1 << n) - 1][n - 1];
return 0;
}
树形DP
AcWing 285. 没有上司的舞会
- 知识点:树形DP、dfs
- 状态表示:f[i][0]表示节点i不选,节点i为根的子树的所有方案,f[i][1]表示选节点i,节点i为根的子树的所有方案,值为快乐指数最大值。
- 状态计算:
- f[u][1] = happy[u] + sum(f[j][0] | j 为u的子节点)
- f[u][0] = sum(max(f[j][0], f[j][1]) | j 为u的子节点)
#include<iostream>
#include<cstring>
using namespace std;
const int N = 6100;
int f[N][2], happy[N], n, fa[N];
int e[N], ne[N], h[N], idx;
void add(int a, int b)
{
e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}
void dfs(int u)
{
f[u][1] = happy[u];
for(int i = h[u]; ~i; i = ne[i])
{
int j = e[i];
dfs(j);
f[u][1] += f[j][0];
f[u][0] += max(f[j][0], f[j][1]);
}
}
int main()
{
memset(h, -1, sizeof h);
cin >> n;
for(int i = 1; i <= n; i++)cin >> happy[i];
for(int i = 0; i < n - 1; i++)
{
int a, b;
cin >> a >> b;
add(b, a);
fa[a]++;
}
int root = 1;
while(fa[root])root++;
dfs(root);
int ans = max(f[root][0], f[root][1]);
cout << ans;
return 0;
}