一个玄妙状压
解析在代码里
/*
感到恐怖,为什么会花式爆int
*/
#include<iostream>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<cstdio>
using namespace std;
const int maxn = 15;
const int INF = 0x3f3f3f3f;
int all,n,m;
int G[1<<maxn]; // 当前集合的可拓展最大集合,包括自身
long long dp[1<<maxn][maxn],dis[maxn][maxn];
int main()
{
cin>>n>>m;
all=(1<<n)-1;
memset(dis,0x3f,sizeof(dis));
memset(dp,0x3f,sizeof(dp));
for(int i=1;i<=m;i++)
{
long long x,y,z;
cin>>x>>y>>z;
dis[x][y]=dis[y][x]=min(dis[x][y],z);// 初始化距离
}
for(int i=1;i<=all;i++)
for(int j=1;j<=n;j++)
if(( i|(1<<(j-1))) ==i ) // 如果在当前的要拓展集合内
{
dis[j][j]=0; // 自己到自己肯定为0
for(int k=1;k<=n;k++)
if(dis[j][k]!=INF)G[i]|=(1<<(k-1)); // 预处理 G 集合
}
for(int i=1;i<=n;i++)dp[1<<(i-1)][0]=0; // dp初始化,每个叶节点拓展第一层都为0
// dp含义,当前树高为j,要拓展成包括
/*for(int i=1;i<=all;i++,cout<<endl) // 集合i所有节点的树所付出的最小代价
for(int j=0;j<=n;j++)cout<<dp[i][j]<<" ";*/
for(int i=2;i<=all;i++)
for(int son=i-1;son;son=(son-1)&i) // 达到遍历i所有子集的目的,因为-1 和 & 操作都是减小,且可以跳过无用部分
if(G[son]|i==G[son])
{
int sson=son^i;
long long sum=0;
for(int j=1;j<=n;j++)
if((1<<(j-1)) & sson)
{
long long mindis=INF;
for(int k=1;k<=n;k++)
{
if((1<<(k-1))&son)
mindis=min(mindis,dis[j][k]); // 取两个集合的最小距离
}
sum+=mindis;
//cout<<sum<<endl;
}
for(int j=1;j<n;j++) // dp
{
if(dp[son][j-1]!=INF)dp[i][j]=min((long long)dp[i][j],(long long)dp[son][j-1]+(long long)sum*j);
//cout<<dp[son][j-1]<<" "<<sum*j<<" "<<dp[i][j]<<endl;
}
}
long long ans=INF;// 取答案
for(int i=0;i<n;i++)if(dp[all][i]!=INF)ans=min(dp[all][i],ans);
cout<<ans;
}