P3044题解
题目链接
这道题还是比较适合练习dijikstra的.
首先我们看到题目.一般来说这种过于复杂的题都会有一个突破口,这里的突破口城镇数量最多为5.我们可以想到一个暴力,那就是分别枚举每一个不是城镇的点看是不是最优的点.但是访问城镇的顺序不是固定的,我们因此使用全排列进行暴力枚举.这样的复杂度是O(120n),虽然常数很恐怖但还是可以过的.
如何求出结果呢?我们只需要维护和城镇相连的点.我们可以维护一个类似floyd的dis数组,然后对每个城镇跑一次dijikstra,这样子就可以得出所有需要计算的距离.
代码:
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e4+10;
int n,m,k,st[N],id[N],dis[10][N],x,y,w,a[N],ans,mi=(1e18);
bool f[N];
struct node{int u,d;bool operator < (const node &u) const{return u.d<d;}
};
vector<node>v[N];
void dij(int x){priority_queue<node>q;for(int i=1;i<=n;i++) dis[x][i]=(1e9), f[i]=0;dis[x][st[x]]=0, q.push({st[x],0});while(q.size()){node t=q.top();q.pop();if(f[t.u]) continue;else f[t.u]=1;for(int i=0;i<v[t.u].size();i++){int u=v[t.u][i].u, w=v[t.u][i].d;if(dis[x][t.u]+w<=dis[x][u]){dis[x][u]=dis[x][t.u]+w;q.push({u,dis[x][u]});}}}
}
signed main(){cin>>n>>m>>k;for(int i=1;i<=k;i++) cin>>st[i];sort(st+1,st+k+1);for(int i=1;i<=k;i++) id[st[i]]=i;while(m--){cin>>x>>y>>w;v[x].push_back({y,w}), v[y].push_back({x,w}); }for(int i=1;i<=k;i++) dij(i);for(int i=1;i<=n;i++){if(id[i]) continue;for(int j=1;j<=k;j++) a[j]=st[j];do{ans=0;for(int j=1;j<k;j++) ans+=dis[id[a[j]]][a[j+1]];ans+=dis[id[a[1]]][i]+dis[id[a[k]]][i];mi=min(mi,ans);}while(next_permutation(a+1,a+k+1));}cout<<mi;return 0;
}