分层图最短路

所谓分层图,就是将图分层,层与层之间的相关顶点使用一条有向边进行连接。这条边可以根据题目给的条件来确定,比如花费为0,花费减小一半...构建k+1层图(k题目会给出,代表可以选择的次数)。当走完这k+1层图后代表所有的选择都已经完成。只要找出起点到终点的最短路就可以求出答案。


板子题:P4568 [JLOI2011] 飞行路线

 以样例为例(使用 @EternalAlexander 这位dalao的OI Painter绘制):

 本次图片引用仅用于学习交流,侵删。

题意上只有五个顶点,但是我们考虑分层图后构建k+1层图,每一层之间的顶点是对应的关系,例如在本图中0号顶点对应的是5号顶点,这两个点都代表同一个地方,但状态不同(5号顶点已经免费乘坐过飞机了)。k在题意中是免费乘坐次数,每当从上一层走到下一层就代表免费乘坐了一次飞机。如此一来只要把这些边全部加入邻接表跑一遍Dijkstra or spfa就好了。

C++ code:

#include <bits/stdc++.h>
using namespace std;
typedef pair<int,int> PII;
const int mx=1e7+50;
int n,m,k,s,ed,dist[mx],vis[mx];
int e[mx],w[mx],h[mx],ne[mx],idx;
void add(int a,int b,int c){
    e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++;
}
void dijkstra(int s){
    priority_queue<PII,vector<PII>,greater<PII>> q;
    dist[s]=0;
    q.push({0,s});
    while(q.size()){
        int u=q.top().second;
        q.pop();
        if(vis[u]) continue;
        vis[u]=1;
        for(int i=h[u];~i;i=ne[i]){
            int j=e[i];
            if(dist[j]>dist[u]+w[i]){
                dist[j]=dist[u]+w[i];
                q.push({dist[j],j});
            }
        }
    }
}
int main()
{
    ios::sync_with_stdio(false);
    cin>>n>>m>>k>>s>>ed;
    memset(h,-1,sizeof h);
    memset(dist,0x3f3f3f,sizeof dist);
    for(int i=0;i<m;i++){
        int a,b,c;
        cin>>a>>b>>c;
        add(a,b,c),add(b,a,c);  //此处加入的是原本存在的边
        for(int j=1;j<=k;j++){
            add(a+j*n,b+j*n,c); //此处加入的是k个图层的边
            add(b+j*n,a+j*n,c);
            add(a+(j-1)*n,b+j*n,0);  //层与层之间的边
            add(b+(j-1)*n,a+j*n,0);
        }
    }
    for(int i=1;i<=k;i++)
        add(ed+(i-1)*n,ed+i*n,0); //将所有的终点用0边连起来
    dijkstra(s);
    int ans=INT_MAX;
    for(int i=0;i<=k;i++)  //理论上取所有的终点中最小和把所有的边用0边连起来一样的
        ans=min(ans,dist[ed+i*n]);//但是这里不这么干似乎WA了
    cout<<dist[ed+k*n]<<endl;
}

相似的题:

P2939 [USACO09FEB]Revamping Trails G

C++ code:

#include <bits/stdc++.h>
using namespace std;
typedef pair<int,int> PII;
const int mx=1e5+10;
int n,m,k,s,ed,dist[mx*20],vis[mx*20];
int e[mx*50],w[mx*50],h[mx*50],ne[mx*50],idx;
void add(int a,int b,int c){
    e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++;
}
void dijkstra(int s=1){
    priority_queue<PII,vector<PII>,greater<PII>> q;
    dist[s]=0;
    q.push({0,s});
    while(q.size()){
        int u=q.top().second;
        q.pop();
        if(vis[u]) continue;
        vis[u]=1;
        for(int i=h[u];~i;i=ne[i]){
            int j=e[i];
            if(dist[j]>dist[u]+w[i]){
                dist[j]=dist[u]+w[i];
                q.push({dist[j],j});
            }
        }
    }
}
int main()
{
    ios::sync_with_stdio(false);
    cin>>n>>m>>k;
    memset(h,-1,sizeof h);
    memset(dist,0x3f3f3f,sizeof dist);
    for(int i=0;i<m;i++){
        int a,b,c;
        cin>>a>>b>>c;
        add(a,b,c),add(b,a,c);
        for(int j=1;j<=k;j++){
            add(a+j*n,b+j*n,c);
            add(b+j*n,a+j*n,c);
            add(a+(j-1)*n,b+j*n,0);
            add(b+(j-1)*n,a+j*n,0);
        }
    }
    dijkstra();
    int ans=INT_MAX;
    for(int i=0;i<=k;i++)
        ans=min(ans,dist[n+i*n]);
    cout<<ans<<endl;
}

 P3831 [SHOI2012]回家的路(可用spfa)

C++ code:

#include <bits/stdc++.h>
using namespace std;
const int M=3e6+50;
int n,m,s,ed,dist[M],vis[M];
int e[M],w[M],ne[M],h[M],idx;
queue<int> q;
struct po{
    int x,y,id;
}point[M];
bool cmpx(po a,po b){
    return a.x==b.x?a.y<b.y:a.x<b.x;
}
bool cmpy(po a,po b){
    return a.y==b.y?a.x<b.x:a.y<b.y;
}
void add(int a,int b,int c){
    e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++;
}
int spfa(int s){
    memset(dist,0x3f,sizeof dist);
    dist[s]=0;
    vis[s]=1;
    q.push(s);
    while(q.size()){
        int u=q.front();
        q.pop();
        vis[u]=0;
        for(int i=h[u];~i;i=ne[i]){
            int j=e[i];
            if(dist[j]>dist[u]+w[i]){
                dist[j]=dist[u]+w[i];
                if(!vis[j])
                    q.push(j),vis[j]=1;
            }
        }
    }
    return dist[ed]==0x3f?-1:dist[ed];
}
int main()
{
    cin>>n>>m;
    memset(h,-1,sizeof h);
    n=m+2,s=n-1,ed=n;
    for(int i=1,a,b;i<=n;i++){
        cin>>a>>b;
        point[i]={a,b,i};
    }
    sort(point+1,point+n+1,cmpx);
    for(int i=1;i<n;i++){
        if(point[i].x==point[i+1].x)
            add(point[i].id,point[i+1].id,abs(point[i].y-point[i+1].y)<<1),
            add(point[i+1].id,point[i].id,abs(point[i].y-point[i+1].y)<<1);
    }
    sort(point+1,point+n+1,cmpy);
    for(int i=1;i<n;i++){
        if(point[i].y==point[i+1].y)
            add(point[i].id+n,point[i+1].id+n,abs(point[i].x-point[i+1].x)<<1),
            add(point[i+1].id+n,point[i].id+n,abs(point[i].x-point[i+1].x)<<1);
    }
    for(int i=1;i<=n-2;i++)
        add(i,i+n,1),add(i+n,i,1);
    add(s,s+n,0),add(s+n,s,0),add(ed,ed+n,0),add(ed+n,ed,0);
    cout<<spfa(s)<<endl;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值