055.多层图最短路(扩点)

扩点最短路,也叫分层图最短路

  • 建图的节点不是真实的位置,而是真实位置+在此处的状态

  • 一般还要用到状态压缩技巧

  • 核心在于如何扩点,如何到达,如何算距离

习题

01 获取所有钥匙的最短路

leetcode 864

  • 节点表示状态 : 真实位置 + 已获取的钥匙

  • 钥匙状态压缩,二进制下对应位的1,0表示该钥匙的有,无

 class Solution {
    vector<int>mo{-1,0,1,0,-1};
    struct e{int x,y,s;};
public:
    int shortestPathAllKeys(vector<string>& g) {
        int n=g.size();
        int m=g[0].size();
        int sx,sy;
        int end=0;
        for(int i=0;i<n;++i){
            for(int j=0;j<m;++j){
                if(g[i][j]=='@')sx=i,sy=j;
                if(g[i][j]>='a'&&g[i][j]<='f'){
                    end|=(1<<(g[i][j]-'a'));
                }
            }
        }
        vector<vector<vector<bool>>>vis(n,vector<vector<bool>>(m,vector<bool>(1<<6,0)));
        queue<e>q;
        int step=-1;
        q.push({sx,sy,0});
        while(q.size()){
            step++;
            int siz=q.size();
            for(int j=0;j<siz;++j){
                auto [x,y,s]=q.front();
                q.pop();
                if(s==end)return step;
                for(int i=0;i<4;++i){
                    int nx=x+mo[i];
                    int ny=y+mo[i+1];
                    int ns=s;
                    if(nx<0||ny<0||nx>=n||ny>=m||g[nx][ny]=='#')continue;
                    if(g[nx][ny]>='A'&&g[nx][ny]<='F'){
                        int t=g[nx][ny]-'A';
                        if((ns&(1<<t))==0)continue;
                    }
                    if(g[nx][ny]>='a'&&g[nx][ny]<='f'){
                        ns|=(1<<(g[nx][ny]-'a'));
                    }
                    if(vis[nx][ny][ns]==0){
                        vis[nx][ny][ns]=1;
                        q.push({nx,ny,ns});
                    }
                }    
            }
        }
        return -1;
    }
};

02 电动车游城市

leetcode 35

  • 节点表示状态 : 真实位置 + 当前电量

  • 每次只考虑充一格电或者不充(充两格电 == 这次充一格 + 下次再充一格 )

class Solution {
    struct e{
        int city;
        int power;
        int cost;
    };
public:
    int electricCarPlan(vector<vector<int>>& paths, int cnt, int start, int end, vector<int>& charge) {
        int n=charge.size();
        vector<vector<pair<int,int>>>gra(n);
        for(auto p:paths){
            gra[p[0]].push_back({p[2],p[1]});
            gra[p[1]].push_back({p[2],p[0]});
        }
        vector<vector<int>>dis(n,vector<int>(cnt+1,0x3f3f3f3f));
        vector<vector<int>>vis(n,vector<int>(cnt+1,0));
        auto cmp=[](e a,e b){return a.cost>b.cost;};
        priority_queue<e,vector<e>,decltype(cmp)>pq(cmp);
        pq.push({start,0,0});
        dis[start][0]=0;
        while(pq.size()){
            auto [u,power,c]=pq.top();
            pq.pop();
            if(u==end)return c;
            if(vis[u][power]==0){
                vis[u][power]=1;
                if(power<cnt&&dis[u][power+1]>c+charge[u]){
                    dis[u][power+1]=c+charge[u];
                    pq.push({u,power+1,c+charge[u]});
                }
                for(auto [w,v]:gra[u]){
                    int rest=power-w;
                    int nc=c+w;
                    if(rest>=0&&dis[v][rest]>nc){
                        dis[v][rest]=nc;
                        pq.push({v,rest,nc});
                    }
                }
            }
        }
        return -1;
    }
};

03 飞机路线

luogu P4568

  • 节点表示状态 : 真实位置 + 已使用的免费次数

  • 每次选择使用免费机会或不使用

const int N=1e4+5;
const int INF=0x3f3f3f3f;

int dis[N][11];
bool vis[N][11];
vector<pair<int,int>>gra[N];

struct st{
    int cur;
    int cnt;
    int cost;
};

void solve(){
    int n,m,k,s,e,u,v,w;
    cin>>n>>m>>k;
    for(int i=0;i<n;++i){
        gra[i].clear();
        for(int j=0;j<=k;j++){
            dis[i][j]=INF;
            vis[i][j]=0;
        }
    }
    cin>>s>>e;
    while(m--){
        cin>>u>>v>>w;
        gra[u].push_back({w,v});
        gra[v].push_back({w,u});
    }
    auto cmp=[](st a,st b){return a.cost>b.cost;};
    priority_queue<st,vector<st>,decltype(cmp)>pq(cmp);
    pq.push({s,0,0});
    dis[s][0]=0;
    while(pq.size()){
        auto [u,cnt,c]=pq.top();
        pq.pop();
        if(u==e){
            cout<<c;
            return;
        }
        if(vis[u][cnt]==0){
            vis[u][cnt]=1;
            for(auto [w,v]:gra[u]){
                if(cnt<k&&c<dis[v][cnt+1]){
                    dis[v][cnt+1]=c;
                    pq.push({v,cnt+1,c});
                }
                int nc=c+w;
                if(nc<dis[v][cnt]){
                    dis[v][cnt]=nc;
                    pq.push({v,cnt,nc});
                }
            }
        }
    }
}

04 K站中转内最便宜的航班

  • 节点表示状态 : 真实位置 + 已经过站数

  • 可以经过k个站,可以选k+1条边

leetcode 787

class Solution {
    struct st{
        int cur;
        int cnt;
        int cost;
    };
public:
    int findCheapestPrice(int n, vector<vector<int>>& edges, int s, int e, int k) {
        vector<vector<pair<int,int>>>gra(n);
        for(auto e:edges){
            gra[e[0]].push_back({e[2],e[1]});
        }
        vector<vector<int>>dis(n,vector<int>(k+2,0x3f3f3f3f));
        vector<vector<int>>vis(n,vector<int>(k+2,0));
        auto cmp=[](st a,st b){return a.cost>b.cost;};
        priority_queue<st,vector<st>,decltype(cmp)>pq(cmp);
        pq.push({s,0,0});
        dis[s][0]=0;
        while(pq.size()){
            auto [u,cnt,c]=pq.top();
            pq.pop();
            if(u==e)return c;
            if(cnt==k+1)continue;
            if(vis[u][cnt]==0){
                vis[u][cnt]=1;
                for(auto [w,v]:gra[u]){
                    int nc=c+w;
                    if(dis[v][cnt+1]>nc){
                        dis[v][cnt+1]=nc;
                        pq.push({v,cnt+1,nc});
                    }
                }
            }
        }
        return -1;
    }
};
posted @ 2026-01-21 00:25  射杀百头  阅读(2)  评论(0)    收藏  举报