题意:
约翰一共有 N 个牧场.由 MM 条布满尘埃的小径连接。小径可以双向通行。每天早上约翰从牧场 1 出发到牧场 N 去给奶牛检查身体。
通过每条小径都需要消耗一定的时间。约翰打算升级其中 K 条小径,使之成为高速公路。在高速公路上的通行几乎是瞬间完成的,所以高速公路的通行时间为 0。
请帮助约翰决定对哪些小径进行升级,使他每天从 1 号牧场到第 N 号牧场所花的时间最短。
题解:
参考题解“”
一直知道分层图,但是没用过,突然遇到一道分层图的网络流题目,发现自己忘了分层图咋实现,赶紧拿来一道分层图模板题做做
简单说说什么是分层图:
其实就是将一个平面的图重新建图,有好几层
具体的说每层图之间各自连边与原图一样,但是相邻的两层图之间根据原来的关系进行连边
虽然是多层图,但是用一维的关系就可以
比如:
当有3个点,3层图时
1对应的就是 1+n 和 1+2 * n
2对应的就是 2+n 和 2+2 * n
…
当存在n个点,k层图时,
1对应的就是1+n * 0, 1+ n* 1…1+n * (k-1)
而且每一层的图都遵循只能从上面的图到下一层图,不能反过来
那建这么多层图的目的是什么呢?
你可以理解成是一种尝试,就比如本题,我们向下一层就代表改造一条道路
我们向下一层代表改造一条道路,我们不可能改造道路后再把它修回原来的样子
我们要建多少层?
有k次机会,不难想出,我们要建k+1层
代码:
#include<cstdio>
#include<queue>
#define read(x) scanf("%d",&x)//宏定义,个人习惯
#define INF 0x3f3f3f3f//伪极大值
using namespace std;
typedef pair<int,int> pii;//个人习惯
struct Node
{int head,dis;
}node[210100];//数组大小注意
struct Edge
{int to,len,next;
}edge[4200100];//数组大小注意
int n,m,k,u,v,w,cnt,ans=INF<<1;
void addEdge(int u,int v,int w)
{edge[++cnt]={v,w,node[u].head};node[u].head=cnt;
}
//链式前向星存图
void Dijkstra()
{for(int i=1;i<=n*(k+1);i++){node[i].dis=INF;}//初始化时,要注意我们的点数已经不是n了,而是n*(k+1)node[1].dis=0;priority_queue<pii,vector<pii>,greater<pii> >q;//小根堆q.push({0,1});while(q.size()){pii tmp=q.top();q.pop();int d=tmp.first,u=tmp.second;if(d!=node[u].dis)continue;for(int e=node[u].head;e;e=edge[e].next){int v=edge[e].to;if(node[v].dis>edge[e].len+d){node[v].dis=edge[e].len+d;q.push({node[v].dis,v});}}}
}
//最短路板子不解释
int main()
{read(n),read(m),read(k);for(int i=1;i<=m;i++){read(u),read(v),read(w);for(int j=0;j<=k;j++){/*当j为0时,我们建立的是原图的边当j不为0时,我们建立的是分身的边*/addEdge(u+j*n,v+j*n,w);addEdge(v+j*n,u+j*n,w);//上面两行是每层图之间,自身的点的连线,边权不变if(j==k)break;/*为什么当j==k时,要退出循环呢?因为如果j==k时,还建下面的边,那么就超出范围了可以自行感性理解一下*/addEdge(u+j*n,v+(j+1)*n,0);addEdge(v+j*n,u+(j+1)*n,0);//这两行建立的是层与层之间的边,边权为0}}Dijkstra();//跑最短路for(int i=0;i<=k;i++){ans=min(ans,node[n+i*n].dis);//统计每一层到n距离的最小值}printf("%d\n",ans);//输出答案return 0;
}