Problem
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/b1f02dacda17576282e43244d107b132.png)
Solution
- 状压dp,dp(i,s)表示i为根包含点集s的最小权值和。
- 用floyd预处理出两点间的最短路,dijkstra或spfa也可。
- 分两种情况转移:i度数为1和i度数不为1。
- i度数为1:枚举相邻点j,dp(i,s)=min(dp(i,s),dp(j,s)+dis(i,j))。
- i度数不为1:枚举子集,dp(i,s)=min(dp(i,sub)+dp(i,s^sub))。
- 注意转移状态的次序,从状压dp的角度考虑也有别的转移方法,不过这种最优,时间复杂度n2 * 2k+n*3k。
Code
#include <iostream>
#include <cstdio>
#include <cmath>
using namespace std;
const int inf=1e9;
long long dp[105][1<<10],dis[105][105],n,m,kk,tag[15],mx,st[105],tp;
int main()
{
cin>>n>>m>>kk;
for(int i=1; i<=n; i++)
for(int j=1; j<=n; j++)
dis[i][j]=inf;
for(int i=1; i<=n; i++) dis[i][i]=0;
for(int i=1,u,v,w; i<=m; i++)
{
scanf("%d%d%d",&u,&v,&w);
dis[u][v]=dis[v][u]=min(w,(int)dis[u][v]);
}
for(int i=1; i<=kk; i++)
scanf("%d",&tag[i]);
for(int k=1; k<=n; k++)
for(int i=1; i<=n; i++)
for(int j=1; j<=n; j++)
dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);
for(int i=1; i<=n; i++)
for(int s=1; s<(1<<kk); s++)
for(int j=0; j<kk; j++)
dp[i][s]+=(s>>j&1)*dis[i][tag[j+1]];
for(int s=0; s<(1<<kk); s++)
for(int i=1; i<=n; i++)
for(int j=1; j<=n; j++)
dp[i][s]=min(dp[i][s],dp[j][s]+dis[i][j]);
for(int s=1; s<(1<<kk); s++)
{
for(int i=1; i<=n; i++)
{
for(int sub=s;sub; sub=(sub-1)&s)
dp[i][s]=min(dp[i][s],dp[i][sub]+dp[i][sub^s]);
if(dp[i][s]<inf) st[++tp]=i;
}
for(int i=st[tp];tp;i=st[--tp])
{
for(int j=1;j<=n;j++)
dp[j][s]=min(dp[j][s],dp[i][s]+dis[i][j]);
}
}
mx=inf;
for(int i=1; i<=n; i++)
mx=min(mx,dp[i][(1<<kk)-1]);
cout<<mx;
return 0;
}