055.多层图最短路(扩点)
扩点最短路,也叫分层图最短路
-
建图的节点不是真实的位置,而是真实位置+在此处的状态
-
一般还要用到状态压缩技巧
-
核心在于如何扩点,如何到达,如何算距离
习题
01 获取所有钥匙的最短路
-
节点表示状态 : 真实位置 + 已获取的钥匙
-
钥匙状态压缩,二进制下对应位的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 电动车游城市
-
节点表示状态 : 真实位置 + 当前电量
-
每次只考虑充一格电或者不充(充两格电 == 这次充一格 + 下次再充一格 )
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 飞机路线
-
节点表示状态 : 真实位置 + 已使用的免费次数
-
每次选择使用免费机会或不使用
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条边
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;
}
};
I am the bone of my sword

浙公网安备 33010602011771号