题意:
约翰一共有 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; }