扩点最短路,也叫分层图最短路
-
建图的节点不是真实的位置,而是真实位置+在此处的状态
-
一般还要用到状态压缩技巧
-
核心在于如何扩点,如何到达,如何算距离
习题
获取所有钥匙的最短路
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});}}}}
}