题目
使关键点联通的最小边权
题解
一般关键点很少,用状压
f
i
,
s
<
=
M
i
n
(
f
i
,
t
+
f
i
,
s
−
t
)
f
i
,
s
<
=
M
i
n
(
f
j
,
s
+
v
a
l
j
,
i
)
f_{i,s}<=Min(f_{i,t}+f_{i,s-t})\\ f_{i,s}<=Min(f_{j,s}+val_{j,i})
fi,s<=Min(fi,t+fi,s−t)fi,s<=Min(fj,s+valj,i)
前者状压DP,后者最短路DP
最后统计答案,然后就水了一篇板子
#include<bits/stdc++.h>
using namespace std;
const int N=1e2+10,M=1e3+10,S=1<<10,INF=0x3F3F3F3F;
int n,m,k,id[N];
int head[N],nex[M],to[M],val[M],tot;
void build(int u,int v,int w){tot++;nex[tot]=head[u];to[tot]=v;val[tot]=w;head[u]=tot;}
int f[N][S];
priority_queue<pair<int,int> >q;
bool vis[N];
void dj(int s)
{
memset(vis,0,sizeof(vis));
while(!q.empty())
{
int u=q.top().second;q.pop();
if(vis[u])continue;vis[u]=1;
for(int i=head[u];i;i=nex[i])
{
int v=to[i];
if(f[v][s]>f[u][s]+val[i])
{
f[v][s]=f[u][s]+val[i];
q.push(make_pair(-f[v][s],v));
}
}
}
}
int main()
{
memset(f,0x3F,sizeof(f));
scanf("%d%d%d",&n,&m,&k);
for(int x,y,z,i=1;i<=m;i++)
{
scanf("%d%d%d",&x,&y,&z);
build(x,y,z);build(y,x,z);
}
for(int i=1;i<=k;i++)scanf("%d",&id[i]),f[id[i]][1<<(i-1)]=0;
for(int s=1;s<(1<<k);s++)
{
for(int i=1;i<=n;i++)
{
for(int t=(s-1)&s;t;t=(t-1)&s)
f[i][s]=min(f[i][s],f[i][t]+f[i][s^t]);
if(f[i][s]<INF)q.push(make_pair(-f[i][s],i));
}
dj(s);
}
int ans=INF;
for(int i=1;i<=n;i++)ans=min(ans,f[i][(1<<k)-1]);
printf("%d",ans);
}