列舉子集+預處理最佳化dp+貪心視角轉化成可做dp

potential-star發表於2024-03-22

https://www.acwing.com/problem/content/531/

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

const int N = 12, M = 1 << N, INF = 0x3f3f3f3f;

int n, m;
int d[N][N], g[N][M];
int f[M][N];

int main()
{
    scanf("%d%d", &n, &m);

    memset(d, 0x3f, sizeof d);
    for (int i = 0; i < n; i ++ ) d[i][i] = 0;
    while (m -- )
    {
        int a, b, c;
        scanf("%d%d%d", &a, &b, &c);
        a --, b -- ;
        d[a][b] = d[b][a] = min(d[a][b], c);
    }

    memset(g, 0x3f, sizeof g);
    for (int i = 0; i < n; i ++ )
        for (int j = 0; j < 1 << n; j ++ )
            for (int k = 0; k < n; k ++ )
                if (j >> k & 1)
                    g[i][j] = min(g[i][j], d[i][k]);
                    //預處理加速dp轉移

    memset(f, 0x3f, sizeof f);
    for (int i = 0; i < n; i ++ ) f[1 << i][0] = 0;//利用題目條件:初始道路免費
    
    
    for (int i = 1; i < 1 << n; i ++ )
        for (int j = i - 1 & i; j; j = j - 1 & i)  // 列舉i的子集
        {
            int r = i ^ j, cost = 0;
            for (int k = 0; k < n; k ++ )
                if (j >> k & 1)
                {
                    cost += g[k][r];
                    if (cost >= INF) break;
                }
            if (cost >= INF) continue;
            for (int k = 1; k < n; k ++ )
                f[i][k] = min(f[i][k], f[r][k - 1] + cost * k);
        }

    int res = INF;
    for (int i = 0; i < n; i ++ )
        res = min(res, f[(1 << n) - 1][i]);
    printf("%d\n", res);

    return 0;
}

相關文章