送外卖的小鸽鸽Mannix(k个点的最短路)

发布于 2020-11-01  0 次阅读


题目大意:

送外卖的小鸽鸽Mannix
已知m条无向边和权值,从1点出发送外卖到k个点后回到1点,求最短路

思路:

将所有的出发点全部跑一遍最短路(dijkstra),然后二维数组存储路径后dfs或者状压DP求最短路即可

AC Code:

#include <iostream>
#include <queue>
#include <cstring>
using namespace std;
#define INF 0x3f3f3f3f3f3f3f3f
#define ll long long
#define int long long
// 分别对应边和点
const int M=15;
const int N=2e5 +9;
int n, m, k, S, T, flag[M];
int dis[N], sum, ans, f[M][M];
bool vis[N];
int head[N], cnt;
  
struct Node {
    int u;
    int dis;
    bool operator < (const Node &x) const {
        return dis > x.dis;
    }
    Node(int u, int dis) : u(u), dis(dis) {}
};
  
struct Edge {
    int v, next;
    int w;
} e[2 * N];
  
void add(int u, int v, int w) {
    e[++cnt].v = v;
    e[cnt].w = w;
    e[cnt].next = head[u];
    head[u] = cnt;
}
  
void dijkstra(int s) {
    priority_queue <Node> q;
    for (int i = 1; i <= n; ++i) { 
        dis[i] = INF;
        vis[i] = false;
    }
    dis[s] = 0;
    q.push(Node(s, 0));
    while (!q.empty()) {
        Node temp = q.top();
        q.pop();
        if (vis[temp.u]) {
            continue;
        }
        vis[temp.u] = true;
        for (int i = head[temp.u]; i; i = e[i].next) {
            int v = e[i].v;
            int len = e[i].w;
            if (!vis[v] && dis[v] > dis[temp.u] + len) {
                dis[v] = dis[temp.u] + len;
                q.push(Node(v, dis[v]));
            }
        }
    }
}
  
// dfs求所有可行路,x记录走过的点的个数,fa记录当前位置
void dfs(int x,int fa){
    // dfs终止情况,即为走到倒数第二步,因最后一步位置确定,故直接取最小值即可
    if(x==k){
        ans=min(ans,sum+f[fa][k+1]);
        return;
    }
    // 求得当前fa可到其他点的所有情况
    for(int i=1;i<=k;i++){
        if(!vis[i]){
            vis[i]=1;
            sum+=f[fa][i];
            dfs(x+1,i);
            vis[i]=0;
            sum-=f[fa][i];
        }
    }
}
  
signed main(){
    int u,v,w;
    cin>>n>>m>>k;
    S=T=1;                  // 起点和终点均为1
    flag[0]=S;flag[k+1]=T;  // 0记录起点,k+1记录终点
    for(int i=1;i<=k;i++){
        cin>>flag[i];
    }
    for(int i=1;i<=m;i++){
        cin>>u>>v>>w;
        add(u,v,w);
        add(v,u,w);
    }
    // 对每个点都用dijkstra求得到其他点的最短路
    for(int i=0; i<=k+1;i++){
        dijkstra(flag[i]);
        for(int j=0;j<=k+1;j++) f[j][i]=f[i][j]=dis[flag[j]]; // 将i到达需要达到的点的距离全部记录进f
    }
    
    memset(vis,0,sizeof(vis));
    ans=INF, sum=0;
    // 将0记为根结点开始dfs求记录数据的最短路和
    dfs(0,0);
    if(ans==INF)    cout<<-1<<endl;
    else            cout<<ans<<endl;
    return 0;
}

平平无奇的在校大学生