题意:找一个经过所有边权值最小的回路,$n le 15$
所有点度数为偶则存在欧拉回路,直接输出权值和
否则考虑度数为奇的点,连着奇数条边,奇点之间走已经走过的路移动再走没走过的路
然后大体想一想就是权值和加上奇点的最小权匹配啦
蒟蒻不会带花树就打了状压$DP$
$f[s]$表示已经选的集合为$s$,考虑当前未选的最小点和哪一个未选的点匹配
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <cmath> using namespace std; typedef long long ll; const int N=18,S=(1<<15)+5,INF=1e9; inline int read(){ char c=getchar();int x=0,f=1; while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();} return x*f; } int n,m,u,v,w,d[N][N],de[N]; void floyd(){ for(int k=1;k<=n;k++) for(int i=1;i<=n;i++) if(d[i][k]<INF) for(int j=1;j<=n;j++) d[i][j]=min(d[i][j],d[i][k]+d[k][j]); } int a[N],p,f[S]; void dp(){ //for(int i=1;i<=p;i++) printf("a %d ",a[i]); memset(f,0x3f,sizeof(f)); f[0]=0; int All=1<<p; for(int s=0;s<All;s++){ int i=0; while((1<<i)&s) i++; for(int j=i+1;j<p;j++) if( !((1<<j)&s) ) f[s|(1<<i)|(1<<j)]=min(f[s|(1<<i)|(1<<j)],f[s]+d[a[i]][a[j]]); } } int main(){ freopen("in","r",stdin); while( (n=read()) ){ m=read(); for(int i=1;i<=n;i++){ de[i]=0; for(int j=1;j<=n;j++) if(i!=j) d[i][j]=INF; } int sum=0; for(int i=1;i<=m;i++){ u=read(),v=read(),w=read(); d[u][v]=d[v][u]=min(d[u][v],w); sum+=w; de[u]++;de[v]++; } p=0; for(int i=1;i<=n;i++) if(de[i]&1) {a[p++]=i;} if(!p) {printf("%d ",sum);continue;} floyd(); dp(); printf("%d ",sum+f[(1<<p)-1]); } }