AcWing 388 Prim + 状态压缩 DP

题意

传送门 AcWing 388 四叶草魔杖

题解

将答案中需要传递能量的节点间连一条边,那么答案为各连通分量上的最小生成树权值之和。 N N N 规模较小, O ( 2 N ) O(2^N) O(2N) 枚举子集,记录满足连通性且节点权值和为 0 0 0 的最小生成树。最后使用状态压缩 D P DP DP 求解答案。设 d p [ i ] dp[i] dp[i] 代表使 i i i 对应的子集各节点能量相同的最小花费,则有 d p [ i ] = d p [ i ⊕ j ] + m s t [ j ] dp[i]=dp[i\oplus j]+mst[j] dp[i]=dp[ij]+mst[j] 其中 j j j i i i 的非空子集, m s t mst mst 代表最小生成树边权和。总时间复杂度 O ( 2 2 N ) O(2^{2N}) O(22N)

#include <bits/stdc++.h>
using namespace std;
const int maxn = 16, inf = 0x3f3f3f3f;
int N, M, A[maxn], G[maxn][maxn], ds[maxn], mst[1 << maxn], dp[1 << maxn];
bool in[maxn], vs[maxn], fg[1 << maxn];

bool prim(int &res, int cnt)
{
    
    
    memset(ds, 0x3f, sizeof(ds));
    memset(vs, 0, sizeof(vs));
    for (int i = 0; i < N; ++i)
        if (in[i])
        {
    
    
            ds[i] = 0;
            break;
        }
    for (int k = 0; k < cnt; ++k)
    {
    
    
        int x = -1;
        for (int i = 0; i < N; ++i)
            if (in[i] && !vs[i] && (x == -1 || ds[i] < ds[x]))
                x = i;
        if (x == -1)
            return 0;
        vs[x] = 1, res += ds[x];
        for (int i = 0; i < N; ++i)
            if (in[i] && !vs[i])
                ds[i] = min(ds[i], G[x][i]);
    }
    return 1;
}

int main()
{
    
    
    scanf("%d%d", &N, &M);
    for (int i = 0; i < N; ++i)
        scanf("%d", A + i);
    memset(G, 0x3f, sizeof(G));
    for (int i = 0; i < N; ++i)
        G[i][i] = 0;
    for (int i = 0, x, y, z; i < M; ++i)
        scanf("%d%d%d", &x, &y, &z), G[x][y] = G[y][x] = z;
    for (int i = 1; i < (1 << N); ++i)
    {
    
    
        memset(in, 0, sizeof(in));
        int sum = 0, cnt = 0;
        for (int j = 0; j < N; ++j)
            if (i >> j & 1)
                sum += A[j], in[j] = 1, ++cnt;
        fg[i] = sum == 0 && prim(mst[i], cnt);
    }
    memset(dp, 0x3f, sizeof(dp));
    dp[0] = 0;
    for (int i = 1; i < (1 << N); ++i)
    {
    
    
        if (!fg[i])
            continue;
        for (int j = i; j; j = (j - 1) & i)
        {
    
    
            if (!fg[j])
                continue;
            dp[i] = min(dp[i], dp[i ^ j] + mst[j]);
        }
    }
    if (dp[(1 << N) - 1] == inf)
        puts("Impossible");
    else
        printf("%d\n", dp[(1 << N) - 1]);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/neweryyy/article/details/114642387