分层图最短路,就是在分层图上解决最短路问题
第一种就是dp的思想:
一般模型为:
在一张图上,有k次机会可以通过一条边而不需要计算权值(免费过路),求从起点到终点的最短路线
常规思路:
想象将一个点拆分为k + 1个点,分别表示到这个点时,免费权消耗了0次,1次,2次......k次
这样实际我们可以把这k个点想象成对应dp的不同的状态
dis[i][j]表示到第i个点时,消耗了j次乘车权后的最短路线
我们用to表示要到达的点,x表示父亲节点,就有
dis[to][j] = min(dis[x][j] + val(x, to), dis[x][j - 1])
因为我们跑最短路时是从前向后跑,也就是当前状态推出后继状态,所以实际上我们可以推出两个可能状态
如果我们消耗了免费过路权
dis[to][j] = min{dis[x][j - 1]}
如果我们没消耗免费过路权
dis[to][j] = min{dis[x][j] + val(x, to)}
这就提醒我们,我们的队列在加入到达某个点的同时,要分别记录到达这个点时的两种不同的状态,以免造成情况遗漏
也就是q[i][j]表示到第i个点时,第i个点在j的情况下我们消耗了几次免费过路权,j为0或是1,0表示没有消耗免费过路权,1表示消耗了免费过路权
到这里我们就能与上面的拆点联系上了,我们想,到了终点时,可能有:用了0次免费过路权,用了1次免费过路权,用了2次,用了3次....用了k次
也就是k+1种可能状态,此时我们把这k+1种状态,每种状态都想象成原本的这个点拆分出来的一个点,也就相当于这个点拆分出了k+1个点,就和上面接上了
然后我们合理外推,对于每一个点都可能出现这样的情况,也就相当于每一个点都拆分成了k+1个点,这n*(k+1)个点之间彼此连接,跑最短路,这样可能有点抽象
实际上把这想象成一个dp的过程是最好理解的
第二种比较容易理解(真正的分层图):
容易卡内存
例题:
第一种做法(dp的思想):
题目描述
给定一张地图一共有 n 个城市,城市编号为 1 ~ n - 1,这 n 个城市通过 m
条铁路连接(走一条铁路视为乘车一次)。而小A 想从城市 s 出发,到 达城市 t
结束。小A 可以免费乘车 k 次,现在他想知道,他这 次旅游的最少花费是多少?
输入格式
第一包含三个整数 n, m, k,含义见题目描述。
第二有两个整数 s, t,表示小A 的出发城市和结束城市。
接下来 m 行,每行三个整数 x, y, z,表?在城市 x 和 y 之间有一条铁路相
连,乘车花费为 z。
输出格式
输出一行,一个整数表示答案。
样例输入
5 6 1
0 4
0 1 5
1 2 5
2 3 5
3 4 5
2 3 3
0 2 100
样例输出
8
数据范围
对于 30% 的数据, 2 ≤ n ≤ 50, 1 ≤ m ≤ 300, k = 0;
对于 50% 的数据, 2 ≤ n ≤ 600, 1 ≤ m ≤ 6000, 0 ≤ k ≤ 1;
#include<bits/stdc++.h>
using namespace std;
#define e exp(1)
#define pi acos(-1)
#define mod 1000000007
#define inf 0x3f3f3f3f
#define ll long long
#define ull unsigned long long
#define mem(a,b) memset(a,b,sizeof(a))
int gcd(int a,int b){return b?gcd(b,a%b):a;}
const int maxn=1e5+10;
int n,m,k,s,t;
struct qnode{
int v,dis,level;
qnode(int v=0,int dis=0,int level=0):v(v),dis(dis),level(level) {}
bool operator<(const qnode &r)const
{
return dis>r.dis;
}
};
struct Edge{
int v,w;
Edge(int v=0,int w=0):v(v),w(w) {}
};
vector<Edge> ve[maxn];
ll d[maxn][11];
bool vis[maxn][11];
void addEdge(int u,int v,int w)
{
ve[u].push_back(Edge(v,w));
}
void dijkstra(int start)
{
mem(vis,false);
mem(d,inf);
priority_queue<qnode> q;
d[start][0]=0;
q.push(qnode(start,0,0));
qnode tmp;
while(!q.empty())
{
tmp=q.top();
q.pop();
int u=tmp.v;
int lv=tmp.level;
if(vis[u][lv])continue;
vis[u][lv]=true;
for(int i=0; i<ve[u].size(); i++)
{
int v=ve[u][i].v;
int w=ve[u][i].w;
if(w+d[u][lv]<d[v][lv])
{
d[v][lv]=w+d[u][lv];
q.push(qnode(v,d[v][lv],lv));
}
if(lv<k)
{
if(d[u][lv]<d[v][lv+1])
{
d[v][lv+1]=d[u][lv];
q.push(qnode(v,d[v][lv+1],lv+1));
}
}
}
}
}
int main()
{
while(~scanf("%d%d%d",&n,&m,&k))
{
scanf("%d%d",&s,&t);
for(int i=0; i<=n; i++)ve[i].clear();
for(int i=0; i<m; i++)
{
int u,v,w;scanf("%d%d%d",&u,&v,&w);
addEdge(u,v,w);
addEdge(v,u,w);
}
dijkstra(s);
ll ans=d[t][0];
for(int i=0; i<=k; i++)ans=min(ans,d[t][i]);
printf("%lld\n",ans);
}
return 0;
}
第二种真正的分层:
ACM-ICPC 2018 南京赛区网络预赛 (计蒜客)
There are N cities in the country, and M directional roads from u to v(1≤u,v≤n). Every road has a distance ci. Haze is a Magical Girl that lives in City 1, she can choose no more than K roads and make their distances become 0. Now she wants to go to City N, please help her calculate the minimum distance.
Input
The first line has one integer T(1≤T≤5), then following T cases.
For each test case, the first line has three integers N, M and K.
Then the following M lines each line has three integers, describe a road Ui,Vi,Ci. There might be multiple edges between u and v.
It is guaranteed that N≤100000,M≤200000,K≤10,
0≤Ci≤1e9. There is at least one path between City 1 and City N.
Output
For each test case, print the minimum distance.
解析:这里注意:它路不是双向的,是单向的
这里有一个点说明一下:vector在插入大数据和很多次插入时,会很浪费时间,因为和结构体联系一起了,在vector使用时,都需要调用自身内部函数。所以在大数据时,尽量减少vector使用,可以直接使用指针的方式来代替对vector操作。这就说明大数据容易超时,就像下面的代码,我就是用vector直接超时了。
超时代码:
#include<bits/stdc++.h>
using namespace std;
#define e exp(1)
#define pi acos(-1)
#define mod 1000000007
#define inf 0x3f3f3f3f
#define ll long long
#define ull unsigned long long
#define mem(a,b) memset(a,b,sizeof(a))
int gcd(int a,int b){return b?gcd(b,a%b):a;}
const int maxn=5e6+5;
int n,m,k;
struct node{
int v,w;
node(int v,int w):v(v),w(w) {}
};
struct qnode{
int to,dis;
qnode(int to,int dis):to(to),dis(dis) {}
bool operator <(const qnode &X)const
{
return dis>X.dis;
}
};
ll d[maxn];
bool vis[maxn];
vector<node> ve[maxn];
void dijkstra(int s)
{
priority_queue<qnode> q;
mem(d,inf);
mem(vis,false);
d[s]=0;
q.push(qnode(s,0));
while(!q.empty())
{
qnode tmp=q.top();q.pop();
int u=tmp.to;
if(vis[u])continue;
vis[u]=true;
for(int i=0; i<ve[u].size(); i++)
{
int v=ve[u][i].v;
int w=ve[u][i].w;
if(d[v]>d[u]+w)
{
d[v]=d[u]+w;
q.push(qnode(v,d[v]));
}
}
}
}
void addnode(int u,int v,int w)
{
ve[u].push_back(node(v,w));
}
int main()
{
int T;scanf("%d",&T);
while(T--)
{
scanf("%d%d%d",&n,&m,&k);
for(int i=0; i<maxn; i++)ve[i].clear();
for(int i=0; i<m; i++)
{
int u,v,w;scanf("%d%d%d",&u,&v,&w);
for(int j=0; j<=k; j++)
{
addnode(u+j*n,v+j*n,w);
if(j!=k)
addnode(u+j*n,v+(j+1)*n,0);
}
}
dijkstra(1);
ll ans=inf;
for(int i=0; i<=k; i++)ans=min(ans,d[n+i*n]);
printf("%lld\n",ans);
}
return 0;
}
用链表实现:
#include<bits/stdc++.h>
using namespace std;
#define pi acos(-1)
#define mod 1000000007
#define inf 0x3f3f3f3f
#define ll long long
#define ull unsigned long long
#define mem(a,b) memset(a,b,sizeof(a))
int gcd(int a,int b){return b?gcd(b,a%b):a;}
const int maxn=1e5+5;
const int maxm=3e6+5;
int n,m,k,p=0,head[maxm];
struct node{
int to,w,next;
}e[maxm<<1];
void addnode(int u,int v,int w)
{
e[++p].to=v;e[p].w=w;e[p].next=head[u];head[u]=p;
}
struct qnode{
int to,dis;
qnode(int to,int dis):to(to),dis(dis) {}
bool operator <(const qnode &X)const
{
return dis>X.dis;
}
};
ll d[maxm];
bool vis[maxm];
void dijkstra(int s)
{
priority_queue<qnode> q;
mem(d,inf);
mem(vis,false);
d[s]=0;
q.push(qnode(s,0));
while(!q.empty())
{
qnode tmp=q.top();q.pop();
int u=tmp.to;
if(vis[u])continue;
vis[u]=true;
for(int i=head[u]; i; i=e[i].next)
{
int v=e[i].to;
int w=e[i].w;
if(d[v]>d[u]+w)
{
d[v]=d[u]+w;
q.push(qnode(v,d[v]));
}
}
}
}
int main()
{
int T;scanf("%d",&T);
while(T--)
{
mem(head,0);p=0;
scanf("%d%d%d",&n,&m,&k);
for(int i=0; i<m; i++)
{
int u,v,w;scanf("%d%d%d",&u,&v,&w);
for(int j=0; j<=k; j++)
{
addnode(u+j*n,v+j*n,w);
if(j!=k)
addnode(u+j*n,v+(j+1)*n,0);
}
}
dijkstra(1);
ll ans=inf;
for(int i=0; i<=k; i++)ans=min(ans,d[n+i*n]);
printf("%lld\n",ans);
}
return 0;
}