F - Dragon Ball I ——最短路

X星球上有一个关于龙珠的传说:如果有人收集到七个龙珠,龙神就会出现并帮助你实现愿望。
有一天,你惊讶地发现这个故事可能是真的:你在跳蚤市场发现了一个龙珠雷达!雷达会向你显示X星球上七个龙珠的位置。你想不失时机地检查关于为自己许愿的古老传说的真相!
行星X上总共有 n 个城市,编号从 1 到 n 。你目前在城市 1 。要从一个城市到另一个城市,你可以乘坐任意数量的 m 条双向传送之旅。第 i 条传送门每次使用的成本为 ti 枚硬币,它可以将你传送到城市 ai 和 bi 之间。要收集一颗龙珠,你只需要访问雷达上标示的龙珠所在的城市。同一个城市可能有多颗龙珠;在这种情况下,如果你访问了该城市,你可以一次性捡起所有的龙珠。

输入
第一行输入包含两个空格分隔的整数 n和 m( 1≤n,m ≤2 00000),城市数量和可能的传送行程。然后跟随 包含三个空格分隔整数的 m 行 ai​,bi​,和 ti​ (1 ≤ ai​,bi​ ≤ n,0≤ ti​ ≤ 10000),如上所述,其表示通过传送旅程连接的两个城市,以及使用传送机的成本。然后跟随一行七个空格分隔的整数,代表雷达上显示的七个龙珠的城市ID。每个ID c 满足界 1 ≤ c ≤ n。

输出
打印收集龙珠雷达上显示的所有七个龙珠所需的最低硬币数量。如果无法完成此任务,请打印 −1。

Input1
10 9
1 2 1
2 3 1
3 4 1
4 5 1
5 6 1
6 7 1
7 8 1
8 9 1
9 10 1
1 2 3 4 5 6 7

Output1
6

Input2
5 5
1 2 0
1 3 0
2 3 1
3 4 1
4 5 1
1 2 1 2 3 4 4

Output2
1

解析:
问题就是按照什么顺序来取龙珠,才能花最少的硬币。
从起点开始出发,就算 7 个龙珠都在不同的地方,全排列也不超时,所以枚举排列顺序,计算每次所花的硬币数,取最小。

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
#define ios ios::sync_with_stdio(false),cin.tie(nullptr),cout.tie(nullptr);
int gcd(int a,int b) { return b? gcd(b,a%b) : a; }
typedef pair<int,int> PII;
const double PI=acos(-1.0);
const int N=2e5+10;
int n,m;
struct node
{
    int v,w;
};
vector <node> g[N];
int d[8][N];                                    //一共8条最短路
bool vis[N];
priority_queue <PII,vector<PII>,greater<PII>> q;
void djkstra(int cnt,int x)
{
    memset(vis,0,sizeof vis);
    d[cnt][x]=0;
    q.push({0,x});
    while (q.size())
    {
        int tance=q.top().first;
        int u=q.top().second;
        q.pop();
        if (vis[u]) continue;
        vis[u]=1;
        for (auto [v,w]:g[u])
        {
            if (d[cnt][v]>tance+w)
            {
                d[cnt][v]=tance+w;
                q.push({d[cnt][v],v});
            }
        }
    }
}
int ans;
int a[10],k[10];
void dfs(int u)
{
    if (u>=7)
    {
        int l=0,r=0,sum=0;
        for (int i=0;i<7;i++)
        {
            r=a[i];
            sum +=d[l][k[a[i]]];
            l=a[i];
        }
        ans=min(ans,sum);
        return ;
    }
    for (int i=1;i<=7;i++)
    {
        if (!vis[i])
        {
            a[u]=i;
            vis[i]=1;
            dfs(u+1);
            vis[i]=0;
        }
    }
}
void solve()
{
    cin>>n>>m;
    for (int i=0;i<m;i++)
    {
        int u,v,w;
        cin>>u>>v>>w;
        g[u].push_back({v,w});
        g[v].push_back({u,w});
    }
    memset(d,0x3f,sizeof d);
    djkstra(0,1);
    for (int i=1;i<=7;i++)
    {
        int x;
        cin>>x;
        k[i]=x;                                 //离散化一下,不用去重
        djkstra(i,x);
    }
    memset(vis,0,sizeof vis);
    ans=1e18;
    dfs(0);                                     //全排列,枚举每一种取法
    if (ans==1e18) cout<<-1;
    else cout<<ans;
}
signed main()
{
    ios;
    int T=1;
    //cin>>T;
    while (T--) solve(); 
    return 0;
}