分层最短路

分层最短路

介绍

分层图最短路在最短路的基础上,增加了一个条件:可将 k 条边的权值变为 0.

分析

这种问题我们可以想到一种做法:

分层最短路_第1张图片

如图,我们构建一个多层的图,其中每一层都是和题目中给的图相同.(图中假设k==1)上面的层表示不将任何边的权值变为0,而下面的这一层表示使用了这个机会后的状态.

也就是0号节点可以不使用机会,转移到2号节点,也可以使用机会转移到7号(其实就是二号的镜像节点).最后我们只需要看0到4或者9的路径谁更短即可(实际上只要是正权图,一般都是下面的节点路径短)

基本原理懂了之后,就是怎么实现的问题.

暴力建图

根据刚才的讨论,我们不难发现,只要把分层图构建出来,就能直接跑dijkstra算法,只要将每个点都拆成(k+1)个,然后将层与层之间的联系构建起来,起点是0号,终点是n*(k+1)-1 这种算法是可行的,写起来也更简洁,而且不用修改最短路的算法,只是有一些暴力.

动态规划

参考博客

我们用dp的角度去思考这个问题.我们令dp[i][j]表示从0号到i号节点,经由j次免费转移所耗费的成本 .

设会有 K 次免费机会,则 dis的状态转移方程为:

dis[i][j]=min(dis[father][j−1],dis[father][j]+w)

其中, father 表示节点i的父亲节点(此处表述有歧义,此转移方程发生在对边的松弛过程中,当前边是father -> i,而使用j次机会i号节点可以由father[j]father[j-1]转移).当 j−1>=k时,dis[father][j]=INF

AC代码

洛谷P4568 飞行路线

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#define IN freopen("in.txt","r",stdin);
using namespace std;
typedef long long int ll;
const int maxn = 1e5+10;
const int INF = 0x3f3f3f3f;

struct State
{                  // 优先队列的结点结构体
    int v, w, cnt; // cnt 表示已经使用多少次免费通行权限
    State() {}
    State(int v, int w, int cnt) : v(v), w(w), cnt(cnt) {}
    bool operator<(const State &rhs) const { return w > rhs.w; }
};
struct node
{
    int v;
    int w;
    int next;
    /* data */
}edge[maxn];
priority_queue<State>pq;
int n,t,m,k,u,v,w,s;
int cnt;
bool vis[maxn][20];
int dis[maxn][20];
int head[maxn];

void add(int u,int v,int w){    //链式前向星存边
    edge[cnt] = {v,w,head[u]};
    head[u] = cnt++;
}

void dijkstra()
{
    memset(dis, 0x3f, sizeof(dis));
    dis[s][0] = 0;
    pq.push(State(s, 0, 0)); // 到起点不需要使用免费通行权,距离为零
    while (!pq.empty())
    {
        State top = pq.top();
        pq.pop();
        int u = top.v;
        int nowCnt = top.cnt;
        if (vis[u][nowCnt])
            continue;
        vis[u][nowCnt] = true;

        for (int i = head[u]; ~i; i = edge[i].next)
        {
            int v = edge[i].v, w = edge[i].w;
            if (nowCnt < k && !vis[v][nowCnt+1] && dis[v][nowCnt + 1] > dis[u][nowCnt])
            { // 可以免费通行
                dis[v][nowCnt + 1] = dis[u][nowCnt];
                pq.push(State(v, dis[v][nowCnt + 1], nowCnt + 1));
            }
            if (!vis[v][nowCnt] && dis[v][nowCnt] > dis[u][nowCnt] + w)
            { // 不可以免费通行
                dis[v][nowCnt] = dis[u][nowCnt] + w;
                pq.push(State(v, dis[v][nowCnt], nowCnt));
            }
        }
    }
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    memset(head,-1,sizeof (head));
    memset(vis,0,sizeof(vis));
    cin >> n >> m >> k;
    cin >> s >> t;
    while (m--)
    {
        cin >> u >> v >> w;
        add(u, v, w);
        add(v, u, w); 
    }
    int ans = INF;
    dijkstra();
    for (int i = 0; i <= k; ++i)
        ans = min(ans, dis[t][i]); // 对到达终点的所有情况取最优值
    cout << ans << endl;
    return 0;
}

你可能感兴趣的:(图论,acm)