分层图是一个处理对边存在额外操作的较好的处理方式。
这里引入一道例题,在正常的最短路中,你可以挑选k条路不需要花费时间,那么最小总花费是多少?
存在一种
d
p
dp
dp的处理方式,
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j]表示到第i个节点用了j次免费,
d
p
[
n
]
[
0
k
]
dp[n][0~k]
dp[n][0 k]即为答案。
那么怎么去转移呢,
d
p
[
v
]
[
j
]
=
m
i
n
(
d
p
[
u
]
[
j
−
1
]
,
d
p
[
u
]
[
j
]
+
u
−
>
v
)
dp[v][j]=min(dp[u][j-1],dp[u][j]+u->v)
dp[v][j]=min(dp[u][j−1],dp[u][j]+u−>v)
我们可以把其转换成k+1个图,分别代表用了几次免费机会的图,并且免费是不断用的次数增加的,也就是说第
i
i
i个图和第
j
j
j个图有单向边相连,边权为0,代表用了一次免费机会。
如何去添加这些边呢,就是将
i
−
>
j
i->j
i−>j在每张图都加上,同时
i
−
>
j
+
n
i->j+n
i−>j+n也加上(同样在每张图)。
这里我用
(
(
k
−
1
)
n
+
1
−
>
k
n
)
((k-1)n+1 -> kn)
((k−1)n+1−>kn)表示第
k
k
k层的节点。
简要模板如下:
int n,m,k;
const int maxn = 10000000;
const int maxm = 20000000;
struct Edge{
int from,to;ll dist;
Edge(){}
Edge(int _from,int _to,ll _dist):from(_from),to(_to),dist(_dist){}
};
Edge ed[maxm];int he[maxn],ne[maxm],etop=1;
void insert(int u,int v,ll w){ed[etop]=Edge(u,v,w);ne[etop]=he[u];he[u]=etop++;}
void add_edge(int u,int v,ll w){
for(int i=u,j=v;i<=u+k*n;i+=n,j+=n)insert(i,j,w);
for(int i=u,j=v+n;j<=v+k*n;i+=n,j+=n)insert(i,j,0);
}
ll d[maxn];
bool vis[maxn];
priority_queue<pair<ll, int>, vector<pair<ll, int> >, greater<pair<ll, int> > > q;
void dijkstra(int s){
memset(vis,0,sizeof(vis));
memset(d,0x3f,sizeof(d));
d[s]=0;
q.push(make_pair(0, s));
while(!q.empty()){
int now = q.top().second;
q.pop();
if(!vis[now]){
vis[now] = true;
for(int i = he[now]; i; i = ne[i]){
Edge& e = ed[i];
if(d[e.to] > d[now] + e.dist){
d[e.to] = d[now] + e.dist;
q.push(make_pair(d[e.to], e.to));
}
}
}
}
}
代码很简单,就是加上了边,所以分层图只是一种思想,没必要死记硬背。
但是这样的代码会
m
l
e
mle
mle,主要在于大量使用了一些不必要的点和边。
其实每次我们只需要对相邻层进行转移即可,其他都可以映射到初始图进行。
用
d
[
i
]
[
p
]
d[i][p]
d[i][p]表示到第
i
i
i个点,使用了
p
p
p次免费机会。
每次松弛的时候,对同一层松弛,同时判断是否还有免费机会,如果有,对下一层也松弛。
int n,m,k;
const int maxn = 100000;
const int maxm = 400010;
struct Edge{
int from,to;ll dist;
Edge(){}
Edge(int _from,int _to,ll _dist):from(_from),to(_to),dist(_dist){}
};
Edge ed[maxm];int he[maxn],ne[maxm],etop=1;
void insert(int u,int v,ll w){ed[etop]=Edge(u,v,w);ne[etop]=he[u];he[u]=etop++;}
ll d[maxn][30];
bool vis[maxn][30];
struct node{
int to,level;
ll cost;
friend bool operator < (node a,node b){
return a.cost>b.cost;
}
};
priority_queue<node>q;
void dijkstra(int s){
memset(vis,0,sizeof(vis));
memset(d,0x3f,sizeof(d));
d[s][0]=0;
q.push(node{s,0,0});
while(!q.empty()){
int now = q.top().to,p=q.top().level; //cout<<d[now][p]<<" "<<q.size()<<endl;
q.pop();
if(!vis[now][p]){
vis[now][p] = true;
for(int i = he[now]; i; i = ne[i]){
Edge& e = ed[i];
if(!vis[e.to][p]&&d[e.to][p]>d[now][p]+e.dist){
d[e.to][p]=d[now][p]+e.dist;
q.push(node{e.to,p,d[e.to][p]});
}
if(p<k&&!vis[e.to][p+1]&&d[e.to][p+1]>d[now][p]){
d[e.to][p+1]=d[now][p];
q.push(node{e.to,p+1,d[e.to][p+1]});
}
}
}
}
}