DP——状态压缩DP、树形DP

DP——状态压缩DP、树形DP

  • 状态压缩DP
    • 蒙德里安的梦想
    • 最短Hamilton路径
  • 树形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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值