分层图哎~
简介
一般我们看到在最短路中,有k条边的边权可以变化(变成0或是变成w/2基本可以大概确定是分层图的做法了)
,其实分层图和最短路本质上是一样的,只是在不同层之间连接了一条边,并且根据题目的条件进行转移罢了;
例题
光说理论太枯燥了,直接来看例题:P2939;例题中,可以免费k
条边,那么我们可以建立k
个图(就是k层图,为什么这样干待会儿会解释):
cin >> n >> m >> k;
// STL大法好,我爱STL~!
// 这里因为点的下标是从1开始,所以要开n+1!
vector<vector<PII>> g(n + 1);
while (m--)
{
int a, b, w;
cin >> a >> b >> w;
// 无向图建图
g[a].push_back(PII(b, w));
g[b].push_back(PII(a, w));
}
// k层图-->一定要记得把dist全部初始化为INF,不然会出错
vector<vector<int>> dist(n + 1, vector<int>(k + 1, INF));
那么我们怎么去处理这个分层图呢,其实我们可以发现:每次用掉一个高速公路的机会的时候,可以看成在第k层图和第k-1层图之间建立了一条边,并且这条边的权重为0
,那这样就好办了呀,就相当于是把图的点数扩大了k倍的一个图,并且在这个图上面跑一遍Dijkstra就好了呀~(其实就是通过这种方式来实现转移);
于是代码就有了:
// web:https://www.luogu.com.cn/problem/P2939
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int, int> PII;
typedef pair<ll, ll> PLL;
const int N = 1e5 + 10;
const int INF = 0x3f3f3f3f;
const ll INFL = 1e18;
int n, m, k;
void dijkstra(vector<vector<PII>>& g, vector<vector<int>>& dist)
{
dist[1][k] = 0;
// 这个st存的是每个节点是否确定了最短路(Dijkstra基本知识awa)
vector<vector<bool>> st(n + 1, vector<bool>(k + 1, false));
// 这个优先队列的意思是{距离, {点的下标, 在第k层图(或者可以理解成还有几条告诉公路修建的机会)}}
priority_queue<pair<int, pair<int, int>>, vector<pair<int, pair<int, int>>>, greater<pair<int, pair<int, int>>>>
pq;
pq.push(make_pair(0, make_pair(1, k)));
while (pq.size())
{
auto t = pq.top();
pq.pop();
int ver = t.second.first;
int dk = t.second.second;
if (st[ver][dk])
continue;
st[ver][dk] = true;
for (auto [j, w] : g[ver])
{
// 处理在当前层上的边
if (dist[j][dk] > dist[ver][dk] + w)
{
dist[j][dk] = dist[ver][dk] + w;
pq.push(make_pair(dist[j][dk], make_pair(j, dk)));
}
// 相当于建立了一条边,连这其他层数
if (dk - 1 >= 0 && dist[j][dk - 1] > dist[ver][dk] + 0)
{
dist[j][dk - 1] = dist[ver][dk];
pq.push(make_pair(dist[j][dk - 1], make_pair(j, dk - 1)));
}
}
}
}
signed main()
{
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
cin >> n >> m >> k;
vector<vector<PII>> g(n + 1);
while (m--)
{
int a, b, w;
cin >> a >> b >> w;
g[a].push_back(PII(b, w));
g[b].push_back(PII(a, w));
}
vector<vector<int>> dist(n + 1, vector<int>(k + 1, INF));
dijkstra(g, dist);
int minn = INF;
for (int i = 0; i <= k; i++)
minn = min(minn, dist[n][i]);
cout << minn << endl;
return 0;
}
补充(还不理解的建议看看)
如果还有不明白的建议看看我的另一篇博客,这篇博客就是具象化分层图,就只有两层,并且写得更详细;