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