题目链接:https://ac.nowcoder.com/acm/contest/7818/B
示例
输入
5 5 3
1 2 1
2 3 2
3 4 3
4 5 4
5 2 4
2 3
1 2
5 3
输出
12
分析
题目可以简化为有 k 条路,每条都要按规定走过。
我们可以想到,每条路要走到别的路肯定是从该路的结束点到其他路的开始点, k 最大18,那么我们可以给每条路按照这样的规则连边,然后用状态压缩来得到答案。
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N = 1e4 + 7;
const ll inf = 0x3f3f3f3f;
const ll INF = 1e18;
int n,m,k;
vector<pair<int,int> > mp[N];
ll dis[N];
bool vis[N];
priority_queue<pair<ll,ll> > Q;
void dij(ll &s)
{
int now,nxt;
for(int i=1;i<=n;++i){
dis[i]=INF;
vis[i]=false;
}
while(!Q.empty()) Q.pop();
Q.push(make_pair(0,s)),dis[s]=0;
while(!Q.empty()){
now=Q.top().second;
Q.pop();
if(vis[now]==true) continue;
vis[now]=true;
for(int i=0;i<mp[now].size();++i){
nxt=mp[now][i].first;
if(!vis[nxt]&&dis[nxt]>dis[now]+mp[now][i].second){
dis[nxt]=dis[now]+mp[now][i].second;
Q.push(make_pair(-dis[nxt],nxt));
}
}
}
}
const int P = 270000;
ll d[20][20],dp[P][18];
pair<ll,ll> pi[N];
int main()
{
int f,t,l;
while(scanf("%d%d%d",&n,&m,&k)!=EOF){
for(int i=1;i<=n;++i)
mp[i].clear();
for(int i=0;i<m;++i){
scanf("%d%d%d",&f,&t,&l);
mp[f].push_back(make_pair(t,l));
mp[t].push_back(make_pair(f,l));
}
for(int i=1;i<=k;++i)
scanf("%d%d",&pi[i].first,&pi[i].second);
for(int i = 0;i < 20;++i)
for(int j = 0;j < 20;++j)
d[i][j] = INF;
for(int i = 1;i <= k;++i){
dij(pi[i].first);
for(int j = 1;j <= k;++j)
d[i][j]=dis[pi[j].second];
}
for(int i=0;i<(1<<k);++i)
for(int j=0;j<=k;++j)
dp[i][j]=INF;
for(int i = 0;i < k;++i) dp[1 << i][i] = d[i + 1][i + 1];
ll ans = inf;
for(int i = 1;i < (1 << k);++i){
for(int j = 0;j < k;++j){
if(!(i & (1 << j))) continue;
for(int l = 0;l < k;++l){
if(i & (1 << l)) continue;
dp[i|(1 << l)][l]=min(dp[i|(1 << l)][l],dp[i][j] + d[j + 1][l + 1] + d[l + 1][l + 1]);
}
if(i + 1 == (1 << k)) ans = min(ans,dp[i][j]);
}
}
printf("%lld\n",ans == inf ? -1 : ans);
}
return 0;
}