分层图最短路 乱搞分享

写在前面

  这篇写到的做法并不是真正的分层图,只是一个假的低配版算法。

  【我这么菜哪里会分层图呢?(╯▔皿▔)╯

  动态规划。

问题

  【既然不是真的分层图,那么就不说什么是分层图了。

  该算法能解决的问题:

    在一张带权图上面,有k次机会修改边权【非同一条边,求从起点到终点的最短路。

  假如会分层图的话,就是一个板板。

思考

  那么不会的话怎么办?

  总是会有一些巨巨给予菜鸡福音

  不知是哪位神犇想出来了简单的动态规划,解决了这个问题。

解法

  首先确认,点数 * 修改次数 能够存成一个二维数组。

  然后,定义 dp[ i ][ j ]表示从起点到 j 的最短路【修改 i 次

  观察定义我们可以发现:

    对于每一种情况,我们只需要跟普通的dp一样,分成选与不选两种情况讨论。

    唯一的不同就是,一般的dp是在求 i 的时候,使用 i-1 转移,但是现在是从 i 直接向 i+1 进行转移。

  关于计算我们可以把最短路修改一部分,得到下面的式子

           if(nw.x+tr[i].val<dis[num][y]) {
                dis[num][y]=nw.x+tr[i].val;
                q[num].push((node){dis[num][y],y});
            }
            if(num!=k&&nw.x<dis[num+1][y]) {
                dis[num+1][y]=nw.x;
                q[num+1].push((node){dis[num+1][y],y});
            }    

  这样的话,我们通过k次最短路的计算最后输出dp[ k ][ n ]就ok。

菜题练手

  P2939 改造路

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;

const int maxn=50009;
int n,m,k;
int head[maxn],ecnt;
struct ss{
    int to,nxt,val;
}tr[maxn<<1];

inline void add(int a,int b,int c) {
    tr[++ecnt].nxt=head[a];
    tr[ecnt].to=b;
    tr[ecnt].val=c;
    head[a]=ecnt;
    return;
}

int dis[25][maxn];//经过k次改造之后到i的最短路 
struct node{
    int x,id;
    bool operator < (const node &a) const{
        return x>a.x;
    }
};
priority_queue<node> q[25];
bool vis[25][maxn];

void dij(int num) {
    while(!q[num].empty()) {
        node nw=q[num].top();
        q[num].pop();
        int x=nw.id;
        if(vis[num][x]) 
            continue;
        vis[num][x]=1;
        for(int i=head[x];i;i=tr[i].nxt) {
            int y=tr[i].to;
            if(vis[num][y])
                continue;
            if(nw.x+tr[i].val<dis[num][y]) {
                dis[num][y]=nw.x+tr[i].val;
                q[num].push((node){dis[num][y],y});
            }
            if(num!=k&&nw.x<dis[num+1][y]) {
                dis[num+1][y]=nw.x;
                q[num+1].push((node){dis[num+1][y],y});
            }
        }
    }
    return;
}

int main()
{
    scanf("%d%d%d",&n,&m,&k);
    for(int i=1;i<=m;i++) {
        int a,b,c;
        scanf("%d%d%d",&a,&b,&c);
        add(a,b,c);
        add(b,a,c);
    }
    for(int i=0;i<=k;i++) 
        for(int j=1;j<=n;j++) 
            dis[i][j]=1e9;
    dis[0][1]=0;
    q[0].push((node){0,1});
    for(int i=0;i<=k;i++) 
        dij(i);
    cout<<dis[min(m,k)][n]<<endl;
    return 0;
}
附送 P2939 代码一份

  P2296 寻找道路

  P4822 冻结

  P4568 飞行路线

  P1948 电话线

如果可以还是去学学分层图吧

posted @ 2019-11-05 23:35  鸽子咕  阅读(191)  评论(0编辑  收藏  举报