一句话题意:求必须包含某K条边的回路(回到1),使得总权值最小
转化为权值最小的联通的偶点
令F[i]表示联通状态为i的最小权值,(3^n状压)表示不在联通块内/奇点/偶点,连边时先不考虑必选的边的度数和权值
最后加上必须的边(保证必须的边都被选了)
连完这些边以后考虑剩下的一些奇点,两两配对,G[i]表示状态为i的奇点两两配对的代价(2^n状压)
#include<cstdio>
#include<algorithm>
using namespace std;
int cnt,n,k,last[15],dis[15][15],G[10005],F[2000005],vis[15],a[15],mi[15];
struct node{
int to,next;
}e[1000005];
void add(int a,int b){
e[++cnt].to=b;
e[cnt].next=last[a];
last[a]=cnt;
}
int main(){
scanf("%d%d",&n,&k);
for (int i=0; i<n; i++)
for (int j=0; j<n; j++)
dis[i][j]=1e9;
int ans=0;
for (int i=1; i<=k; i++){
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
x--,y--;
add(x,y);
add(y,x);
ans+=z;
vis[x]++;
vis[y]++;
dis[x][y]=min(dis[x][y],z);
dis[y][x]=min(dis[y][x],z);
}
int m;
scanf("%d",&m);
for (int i=1; i<=m; i++){
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
x--,y--;
dis[x][y]=min(dis[x][y],z);
dis[y][x]=min(dis[y][x],z);
}
for (int k=0; k<n; k++)
for (int i=0; i<n; i++)
for (int j=0; j<n; j++)
dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);
for (int i=0; i<(1<<n); i++) G[i]=1e9;
G[0]=0;
for (int i=0; i<(1<<n); i++)
for (int x=0; x<n; x++)
if (!(i&(1<<x))){
for (int y=x+1; y<n; y++)
if (!(i&(1<<y)))
G[i|(1<<x)|(1<<y)]=min(G[i|(1<<x)|(1<<y)],G[i]+dis[x][y]);
}
mi[0]=1;
for (int i=1; i<=n; i++) mi[i]=mi[i-1]*3;
for (int i=0; i<mi[n]; i++) F[i]=1e9;
F[2]=0;
for (int now=2; now<mi[n]; now++)
if (F[now]!=1e9){
int N=0;
for (int i=0; i<n; i++) if (now/mi[i]%3) a[N++]=i;
for (int i=0; i<n; i++)
if (!(now/mi[i]%3)){
for (int j=last[i]; j; j=e[j].next){
int V=e[j].to;
if (now/mi[V]%3){
int To=now+mi[i]*2;
F[To]=min(F[To],F[now]);
}
}
for (int j=0; j<N; j++){
int To=now+mi[i];
if (now/mi[a[j]]%3==1) To+=mi[a[j]];
else if (now/mi[a[j]]%3==2) To-=mi[a[j]];
F[To]=min(F[To],F[now]+dis[i][a[j]]);
}
}
}
int ANS=1e9;
for (int now=0; now<mi[n]; now++){
int flag=0,Now=now;
for (int i=0; i<n; i++) if (vis[i] && !(Now/mi[i]%3)){
flag=1;
break;
}
if (flag) continue;
for (int i=0; i<n; i++)
if (vis[i]&1){
if (Now/mi[i]%3==1) Now+=mi[i];
else if (Now/mi[i]%3==2) Now-=mi[i];
}
int To=0;
for (int i=0; i<n; i++) if (Now/mi[i]%3==1) To|=(1<<i);
ANS=min(ANS,F[now]+G[To]);
}
printf("%d\n",ans+ANS);
return 0;
}