Jzoj P3470 最短路___分点spfa+dfs

题目大意:

一个 n n 个点 m m 条边的有向图,有 k k 个标记点,要求从规定的起点 S S 按任意顺序经过所有标记点到达规定的终点 T T ,问最短的距离是多少。

n<=50000m<=100000 n <= 50000 , m <= 100000
0<=k<=101<=<=5000 0 <= k <= 10 , 1 <= 边 权 <= 5000

分析:

对于起点 S S 跟每个标记点进行一遍 spfa s p f a
然后我们考虑经过的标记点的顺序,这个利用 dfs d f s 去枚举,有 k! k ! 种情况,
如果需要经过的标记点为 x,y x , y
那么显然路径有可能是
S>x>y>T S − > x − > y − > T 或者 S>y>x>T S − > y − > x − > T > − > 表示间接或者直接地到达,
那么最小距离显然是
min(dis[S][x]+dis[x][y]+dis[y][T]dis[S][y]+dis[y][x]+dis[x][T]) m i n ( d i s [ S ] [ x ] + d i s [ x ] [ y ] + d i s [ y ] [ T ] , d i s [ S ] [ y ] + d i s [ y ] [ x ] + d i s [ x ] [ T ] )
因为假设我们 S>T S − > T 的路径上必须要经过标记点 x x ,那么分成 S>x S − > x 的最短路+ x>T x − > T 的最短路,必定最优。

代码:

#include
#include
#include
#include
#include
#include
#define N 50005

using namespace std;

typedef long long ll;

struct Node { int To, nxt; ll w; }e[2*N];
int vis[N], ls[N], Chose[15], C[15];
int n, m, k, s, t, cnt, tot;
ll dis[15][N], ans = -1;

queue <int> Q;

void Addedge(int u, int v, ll w)
{
    e[++cnt].To = v, e[cnt].w = w, e[cnt].nxt = ls[u], ls[u] = cnt;
}

void spfa(int x)
{
    ++tot;
    for (int i = 1; i <= n; i++) 
         vis[i] = 0, dis[tot][i] = -1;
    dis[tot][x] = 0;
    vis[x] = 1;
    while (Q.size()) Q.pop();
    Q.push(x);
    while (Q.size())
    {
           int u = Q.front();
           Q.pop();
           for (int i = ls[u]; i; i = e[i].nxt) 
           {
                int v = e[i].To;
                if (dis[tot][u] + e[i].w < dis[tot][v] || dis[tot][v] == -1)
                {
                    dis[tot][v] = dis[tot][u] + e[i].w;
                    if (!vis[v]) 
                    {
                         vis[v] = 1;
                         Q.push(v);
                    }
                }
           }
           vis[u] = 0;
    }
}

void dfs(int dep, ll now, int x)
{
    if (dep == k)
    {
        if (now + dis[x][t] < ans || ans == -1) ans = now + dis[x][t];
        return;
    }
    if (dis[x][t] == -1) return;
    if (now + dis[x][t] >= ans && ans != -1) return;
    for (int i = 1; i <= k; i++)
         if (!Chose[i])
         {
              Chose[i] = 1;
              dfs(dep + 1, now + dis[x][C[i]], i);
              Chose[i] = 0;
         }
}

int main()
{
    scanf("%d %d %d %d %d", &n, &m, &k, &s, &t);
    for (int i = 1; i <= m; i++)
    {
         int u, v; ll w;
         scanf("%d %d %lld", &u, &v, &w);
         Addedge(u, v, w);
    }
    tot = -1;
    spfa(s);
    if (k == 0)
    {
        if (dis[0][t] == -1) printf("-1\n");
                         else printf("%lld\n", dis[0][t]);
        return 0;
    }
    for (int i = 1; i <= k; i++)
    {
         scanf("%d", &C[i]);
         spfa(C[i]);
    }
    for (int i = 1; i <= k; i++)
    {
         Chose[i] = 1; 
         dfs(1, dis[0][C[i]], i);
         Chose[i] = 0;
    }
    if (ans == -1) printf("-1\n");
              else printf("%lld\n", ans);
    return 0;
}

你可能感兴趣的:(深搜dfs,C++,spfa)