191028-分层图最短路
分层图
分层图最短路是指在可以进行分层图的图上解决最短路问题。分层图:可以理解为有多个平行的图。
一般模型是:在一个正常的图上可以进行 k 次决策,对于每次决策,不影响图的结构,只影响目前的状态或代价。一般将决策前的状态和决策后的状态之间连接一条权值为决策代价的边,表示付出该代价后就可以转换状态了。
一般有两种方法解决分层图最短路问题:
1,建图时直接建成k+1层。
2,多开一维记录机会信息。
第一种方法
第二种方法
对于刚才的数据,也就会有下方的建图
以上摘抄自 传送门
例题
T1 Telephone Lines
方法1,二分答案+spfa
解析
本题的的答案显然具有单调性,因为支付的钱更多时,合法的升级方案一定包含了花费更少的方案,所以我们可以将问题转换为:是否存在一条合法的升级方法,使花费不超过mid
因此每次二分出答案后,只需要把升级价格大于mid的电缆看做长度为1的路径,其余的看做长度为0的路径,然后跑一遍spfa,如果d[n]<=k 则返回true 否则返回false(另外针对边权只有0,1的最短路,也可以用双端bfs)
代码1
#include<bits/stdc++.h>
using namespace std;
int first[1001],to[20001],nxt[20001],w[20001],n,m,d[1001],tot,k;
bool vis[1001],bj;
void add(int x,int y,int z)
{
nxt[++tot]=first[x];
first[x]=tot;
to[tot]=y;
w[tot]=z;
}
void spfa(int r)
{
queue<int>q;
memset(d,127,sizeof(d));
memset(vis,0,sizeof(vis));
d[1]=0;
q.push(1);
while(!q.empty())
{
int v=q.front();
vis[v]=0;
q.pop();
for(int i=first[v];i;i=nxt[i])
{
int p=0;
int u=to[i];
if(w[i]>r) p=1;
if(d[u]>d[v]+p)
{
d[u]=d[v]+p;
if(!vis[u])
{
q.push(u);
vis[u]=1;
}
}
}
}
}
bool check(int r)
{
spfa(r);
if(d[n]<=k) return 1;
return 0;
}
int main()
{
int x,y,z[10001];
scanf("%d%d%d",&n,&m,&k);
for(int i=1;i<=m;i++)
{
scanf("%d%d%d",&x,&y,&z[i]);
add(x,y,z[i]);
add(y,x,z[i]);
}
sort(z+1,z+m+1);
int l=0;
int r=m;
while(l<r)
{
int mid=(l+r)/2;
if(check(z[mid]))
{
r=mid;
bj=1;
}
else
l=mid+1;
}
if(!bj) printf("-1\n");
else printf("%d",z[r]);
return 0;
}
方法2 分层图最短路
解析
分层图一般用来解决特殊的最短路问题
代码2
#include<bits/stdc++.h>
#define M 3000009
using namespace std;
const long long INF=1000000009;
int nxt[M*2],first[M],to[M],w[2*M],d[M],n,m,tot,k;
bool vis[M];
priority_queue<pair<int,int> >q;
void add(int x,int y,int z)
{
nxt[++tot]=first[x];
first[x]=tot;
to[tot]=y;
w[tot]=z;
}
void dj()
{
memset(d,127,sizeof(d));
d[1]=0;
vis[1]=1;
q.push(make_pair(-d[1],1));
while(!q.empty())
{
int v=q.top().second;
q.pop();
for(int i=first[v];i;i=nxt[i])
{
int u=to[i];
if(d[u]>max(d[v],w[i]))//求的是第k+1大边的最小值
{
d[u]=min(d[u],max(d[v],w[i]));
if(!vis[u])
q.push(make_pair(-d[u],u));
}
}
}
}
int main()
{
int x,y,z;
scanf("%d%d%d",&n,&m,&k);
for(int i=1;i<=m;i++)
{
scanf("%d%d%d",&x,&y,&z);
for(int j=0;j<=k;j++)
{
add(x+j*n,y+j*n,z);
add(y+j*n,x+j*n,z);
if(j!=k)
{
add(x+j*n,y+(j+1)*n,0);
add(y+j*n,x+(j+1)*n,0);//分层图建边
}
}
}
dj();
long long ans=INF;
for(int i=0;i<=k;i++)
ans=min(ans,(long long)d[n+i*n]);
if(ans==INF)
printf("-1\n");
else
printf("%lld\n",ans);
return 0;
}
T2 飞行路线
分层图最短路代码
#include<bits/stdc++.h>
#define M 3000009
using namespace std;
int nxt[M*2],first[M],to[2*M],w[2*M],n,m,tot,k,s,t;
long long d[M];
bool vis[M];
priority_queue<pair<int,int> >q;
void add(int x,int y,int z)
{
nxt[++tot]=first[x];
first[x]=tot;
to[tot]=y;
w[tot]=z;
}
void dj(int r)
{
memset(d,127,sizeof(d));
d[r]=0;
vis[r]=1;
q.push(make_pair(-d[r],r));
while(!q.empty())
{
int v=q.top().second;
q.pop();
for(int i=first[v];i;i=nxt[i])
{
int u=to[i];
if(d[u]>d[v]+w[i])//求的是最短路
{
d[u]=d[v]+w[i];
if(!vis[u])
q.push(make_pair(-d[u],u));
}
}
}
}
int main()
{
int x,y,z;
scanf("%d%d%d",&n,&m,&k);
scanf("%d%d",&s,&t);
for(int i=1;i<=m;i++)
{
scanf("%d%d%d",&x,&y,&z);
for(int j=0;j<=k;j++)
{
add(x+j*n,y+j*n,z);
add(y+j*n,x+j*n,z);
if(j!=k)
{
add(x+j*n,y+(j+1)*n,0);
add(y+j*n,x+(j+1)*n,0);//??
}
}
}
dj(s);
long long ans=d[t];
for(int i=1;i<=k;i++)
ans=min(ans,(long long)d[t+i*n]);
printf("%lld\n",ans);
return 0;
}
T3 改造路
分层图最短路代码
#include<bits/stdc++.h>
#define M 3000090
using namespace std;
int nxt[M*2],first[M],to[2*M],w[2*M],n,m,tot,k,s,t;
long long d[M];
bool vis[M];
priority_queue<pair<int,int> >q;
void add(int x,int y,int z)
{
nxt[++tot]=first[x];
first[x]=tot;
to[tot]=y;
w[tot]=z;
}
void dj(int r)
{
memset(d,127,sizeof(d));
d[r]=0;
vis[r]=1;
q.push(make_pair(-d[r],r));
while(!q.empty())
{
int v=q.top().second;
q.pop();
for(int i=first[v];i;i=nxt[i])
{
int u=to[i];
if(d[u]>d[v]+w[i])
{
d[u]=d[v]+w[i];
if(!vis[u])
q.push(make_pair(-d[u],u));
}
}
}
}
int main()
{
int x,y,z;
scanf("%d%d%d",&n,&m,&k);
for(int i=1;i<=m;i++)
{
scanf("%d%d%d",&x,&y,&z);
for(int j=0;j<=k;j++)
{
add(x+j*n,y+j*n,z);
add(y+j*n,x+j*n,z);
if(j!=k)
{
add(x+j*n,y+(j+1)*n,0);
add(y+j*n,x+(j+1)*n,0);//??
}
}
}
dj(1);
long long ans=d[n];
for(int i=1;i<=k;i++)
ans=min(ans,(long long)d[n+i*n]);
printf("%lld\n",ans);
return 0;
}
T4 冻结
代码
#include<bits/stdc++.h>
#define M 3000006
using namespace std;
int first[M],tot,nxt[M*2],to[M*2],w[M*2],d[M],n,m,k;
bool vis[M];
priority_queue<pair<int,int> >q;
void add(int x,int y,int z)
{
nxt[++tot]=first[x];
first[x]=tot;
to[tot]=y;
w[tot]=z;
}
void dj()
{
memset(d,127,sizeof(d));
d[1]=0;
vis[1]=1;
q.push(make_pair(-d[1],1));
while(!q.empty())
{
int v=q.top().second;
q.pop();
for(int i=first[v];i;i=nxt[i])
{
int u=to[i];
if(d[u]>d[v]+w[i])
{
d[u]=d[v]+w[i];
if(!vis[u])
q.push(make_pair(-d[u],u));
}
}
}
}
int main()
{
int x,y,z;
scanf("%d%d%d",&n,&m,&k);
for(int i=1;i<=m;i++)
{
scanf("%d%d%d",&x,&y,&z);
for(int j=0;j<=k;j++)
{
add(x+j*n,y+j*n,z);
add(y+j*n,x+j*n,z);
if(j!=k)
{
add(x+j*n,y+(j+1)*n,z/2);
add(y+j*n,x+(j+1)*n,z/2);
}
}
}
dj();
int ans=d[n];
for(int i=1;i<=k;i++)
ans=min(ans,d[n+i*n]);
printf("%d",ans);
return 0;
}