Luogu

分析

状压 DP 。

设 $f_{S,i}$ 表示已经选了 $S$ ,最远距离为 $i$ 的最小代价。

转移枚举一个子集 $T$ ,然后由 $f_{T,i-1}+cost$ 转移到 $f_{S,i}$ 即可。

注意到这里的 $T$ 要能够扩展到 $S$ ,可以预处理出一个 $g_S$ 表示 $S$ 能扩展到的集合来快速判断。

$cost$ 直接对于每个不在 $T$ 中的元素找一个最近的 $T$ 中的点就好了。

边界是 $f_{\{i\},i}=0$ ,答案是 $\min\left\{f_{U,i}\right\}$

代码

// ===================================
//   author: M_sea
//   website: http://m-sea-blog.com/
// ===================================
#include <algorithm>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <cmath>
#define re register
using namespace std;

inline int read() {
    int X=0,w=1; char c=getchar();
    while (c<'0'||c>'9') { if (c=='-') w=-1; c=getchar(); }
    while (c>='0'&&c<='9') X=X*10+c-'0',c=getchar();
    return X*w;
}

const int N=12+10;
const int inf=0x3f3f3f3f;

int n,m,U;
int G[N][N];
int f[1<<12][N],g[1<<12];

int main() {
    n=read(),m=read(),U=1<<n;
    
    memset(G,0x3f,sizeof(G));
    for (re int i=0;i<n;++i) G[i][i]=0;
    for (re int i=1;i<=m;++i) {
        int u=read()-1,v=read()-1,w=read();
        G[u][v]=G[v][u]=min(G[u][v],w);
    }

    for (re int S=1;S<U;++S)
        for (re int i=0;i<n;++i) {
            if (!(S&(1<<i))) continue;
            for (re int j=0;j<n;++j)
                if (G[i][j]!=inf) g[S]|=1<<j;
        }
    
    memset(f,0x3f,sizeof(f));
    for (re int i=0;i<n;++i) f[1<<i][0]=0;
    for (re int S=2;S<U;++S)
        for (re int T=S&(S-1);T;T=(T-1)&S) {
            if ((g[T]|S)!=g[T]) continue;
            int SS=S-T,now=0;
            for (re int i=0;i<n;++i) {
                if (!(SS&(1<<i))) continue;
                int mn=inf;
                for (re int j=0;j<n;++j) {
                    if (!(T&(1<<j))) continue;
                    mn=min(mn,G[j][i]);
                }
                now+=mn;
            }
            for (re int i=1;i<n;++i) {
                if (f[T][i-1]==inf) continue;
                f[S][i]=min(f[S][i],f[T][i-1]+now*i);
            }
        }
    
    int ans=inf;
    for (re int i=0;i<n;++i) ans=min(ans,f[U-1][i]);
    printf("%d\n",ans);
    return 0;
}
最后修改:2019 年 09 月 27 日 01 : 21 PM