[NOIP2017] 寶藏

題目連接ios

題意數組

  題面過長,自行傳送...ide

分析spa

  乍一看就是個搜索剪枝,然而正解是狀壓DPcode

  咱們用二進制表示能夠到達的點的集合,設 $d[i][s]$ 表示從點集 $s$ 直接到達點 $i$ 的最短路徑長度,$f[i][s]$ 表示加入深度爲 $i$ 的點後點集 $s$ 所需的最小代價(深度爲起點到該點最少通過的點數)blog

  首先枚舉 $s$ 處理出 $d$ 數組,而後按照深度依次更新 $f$ 數組,每一個深度下枚舉 $s$ ,取其全部子集直接到達其的最小代價,最後獲得答案get

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <cmath>
using namespace std;
#define ll long long
#define inf 6000000
#define N 13

int n, m, u, v, w, all;
int g[N][N], f[N][1 << N], d[N][1 << N];

int main() {
    scanf("%d%d", &n, &m);
    all = (1 << n) - 1;
    for (int i = 1; i <= n; i++)
        for (int j = 0; j <= all; j++)
            d[i][j] = f[i][j] = inf;
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= n; j++)
            g[i][j] = inf;
    for (int i = 1; i <= n; i++) g[i][i] = 0;
    for (int i = 1; i <= m; i++) {
        scanf("%d%d%d", &u, &v, &w);
        g[u][v] = g[v][u] = min(g[u][v], w);
    }
    for (int s = 0; s <= all; s++)
        for (int i = 1; i <= n; i++)
            if ((1 << (i - 1)) & s)
                for (int j = 1; j <= n; j++)
                    if (!((1 << (j - 1)) & s))
                        d[j][s] = min(d[j][s], g[i][j]);
    for (int i = 1; i <= n; i++) f[1][1 << (i - 1)] = 0;
    for (int i = 2; i <= n; i++)
        for (int s = 0; s <= all; s++)
            for (int k = s; k; k = (k - 1) & s) {
                int tot = 0;
                for (int j = 1; j <= n; j++)
                    if ((1 << (j - 1)) & (k ^ s))
                        tot += d[j][k];
                f[i][s] = min(f[i][s], f[i - 1][k] + (i - 1) * tot);
            }
    printf("%d\n", f[n][all]);

    return 0;
}
View Code
相關文章
相關標籤/搜索