LOJ 2318 「NOIP2017」宝藏

本文深入探讨了状压动态规划(DP)的原理及应用,通过具体实例讲解如何利用状压DP解决复杂问题,特别是在状态空间较大时的优化策略。文章详细分析了状压DP的时间复杂度,并提供了一段C++代码示例,展示了如何实现状压DP求解最小代价问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题面

题目传送门

解法

为什么我的状压dp那么丑啊……

  • 发现 n ≤ 12 n≤12 n12,所以不妨考虑状压dp
  • f [ d ] [ S ] [ r t ] f[d][S][rt] f[d][S][rt]表示当前深度为 d d d,在根为 r t rt rt的子树中有 S S S集合内的点的最小代价
  • 考虑先枚举 r t rt rt的一个儿子是什么,假设为 x x x,然后枚举集合 S ′ ⊂ S S'\subset S SS作为 x x x的子树部分,其它剩下的部分继续作为除 x x x以外的子树部分
  • 可以发现这个dp很好转移: f [ d ] [ S ] [ r t ] = m i n ( d × v [ r t ] [ x ] + f [ d + 1 ] [ S ′ ] [ x ] + f [ d ] [ S − S ′ ] [ r t ] ) f[d][S][rt]=min(d×v[rt][x]+f[d+1][S'][x]+f[d][S-S'][rt]) f[d][S][rt]=min(d×v[rt][x]+f[d+1][S][x]+f[d][SS][rt])
  • 时间复杂度: O ( 3 n n 3 ) O(3^nn^3) O(3nn3)
  • 为什么是这个复杂度呢?因为枚举子集的整体复杂度为 O ( 3 n ) O(3^n) O(3n),这个可以使用二项式定理来证明。关于 n n n的状态为 O ( n 2 ) O(n^2) O(n2),转移的复杂度为 O ( n ) O(n) O(n),所以总复杂度为 O ( 3 n n 3 ) O(3^nn^3) O(3nn3)

代码

#include <bits/stdc++.h>
#define inf 1ll << 40
#define ll long long
#define N 13
using namespace std;
ll lg[1 << N], a[N][N], f[N][1 << N][N];
int lowbit(int x) {return x & -x;}
ll dp(int d, int S, int rt) {
    if (!S) return 0;
    if (f[d][S][rt]) return f[d][S][rt];
    ll ret = inf;
    for (int tx = S, ty = lowbit(S); tx; tx -= ty, ty = lowbit(tx)) {
        int x = lg[ty], y = S ^ ty; ll tmp = a[rt][x] * d;
        for (int s = y; s; s = (s - 1) & y)
            ret = min(ret, tmp + dp(d + 1, s, x) + dp(d, y ^ s, rt));
        ret = min(ret, tmp + dp(d, y, rt));
    }
    return f[d][S][rt] = ret;
}
int main() {
    ios::sync_with_stdio(false);
    int n, m; cin >> n >> m;
    for (int i = 2; i < (1 << n); i++) lg[i] = lg[i >> 1] + 1;
    for (int i = 0; i < n; i++)
        for (int j = 0; j < n; j++)
            a[i][j] = inf;
    for (int i = 1; i <= m; i++) {
        int x, y; ll v; cin >> x >> y >> v;
        x--, y--; ll tx = min(a[x][y], v);
        a[x][y] = a[y][x] = tx;
    }
    ll ans = inf, mask = (1 << n) - 1;
    for (int i = 0; i < n; i++)
        ans = min(ans, dp(1, mask ^ (1 << i), i));
    cout << ans << "\n";
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值