340. 通信线路(分层图 / 二分 + 堆dij)

这篇博客探讨了如何使用二分搜索解决寻找图中第k+1大的边权值的问题,以及通过构建分层图进行最短路径求解。两种方法分别通过不断调整边权值的界限和构建新图进行最短路计算,展示了在复杂图结构中寻找特定路径的有效策略。同时,文章强调了在二分搜索中确保边界条件的正确设定,以及在分层图中利用最大值松弛操作的重要性。
摘要由CSDN通过智能技术生成

340. 通信线路

二分思路:
显然,我们需要找到第 k + 1 k+1 k+1大的边的权值,作为一条最短路的代价来输出。

那么我们就可以对边权值的所有范围来进行二分,但是在这里我们可以看到权值的范围为 1 1 1~ 1 e 6 1e6 1e6,那么 0 0 0的情况存不存在?其实是可能存在的,如果当 k k k的值大于我们要求的路的边数,那么我们的答案就有可能是 0 0 0,也就是说 0 0 0也是我们要二分的答案之一。

再来分析右边界,如果说不存在一条 1 → N 1→N 1N的联通路,那么我们的二分答案一定是 1 e 6 1e6 1e6,但是如果有一组数据,使得我们在将 k k k条边的花费变为 0 0 0后,仍然存在一条边的权值为 1 e 6 1e6 1e6,这时候我们就不能有效的区分该图是不是存在这样一条路。所以说,我们要将右边界设为 1 e 6 + 1 1e6+1 1e6+1

再来分析一下为什么二分是对的

这里二分出来的最大值为什么一定存在在题中给出的所有边中,有没有可能二分出的值不在题目中给出的边中?

肯定是不能的。

因为如果某个满足要求的值不是某条边的权值,那么一定存在一个更小的权值也满足要求,而二分是求出最小的满足要求的值,所以二分出的值必然是某条边的权值。

#include<iostream>
#include<algorithm>
#include<cstring>
#include<string>
#include<map>
#include<cstdio>
#include<cmath>
#include<iomanip>
#include<sstream>
#include<queue>
#define pi 3.1415926535
using namespace std;
typedef long long ll;
const int mod = 1e9 + 7;
typedef pair<int, int> pii;
const int N = 1e6 + 10;
int h[N], e[N], ne[N], idx, w[N];
void add(int a, int b, int c) {
    e[idx] = b, ne[idx] = h[a], w[idx] = c, h[a] = idx++;
}

int n, m, k;
int id[N];
int dis[N];
bool st[N];

int dijkstra(int hh) {
    memset(dis, 0x3f, sizeof dis);
    memset(st, false, sizeof st);
    priority_queue<pii, vector<pii>, greater<pii>> heap;
    heap.push({ 0,1 });
    dis[1] = 0;
    while (heap.size())
    {
        pii t = heap.top();
        heap.pop();
        int ver = t.second;
        if (st[ver])continue;
        st[ver] = true;
        for (int i = h[ver]; ~i; i = ne[i]) {
            int j = e[i];
            int v = w[i] > hh;
            if (dis[j] > dis[ver] + v) {
                dis[j] = dis[ver] + v;
                heap.push({ dis[j], j });
            }
        }

    }
    return dis[n] <= k;
}

int main() {
    memset(h, -1, sizeof h);
    cin >> n >> m >> k;
    while (m--)
    {
        int a, b, c;
        cin >> a >> b >> c;
        add(a, b, c);
        add(b, a, c);
    }
    int l = 0, r = 1e6 + 1;
    while (l < r)
    {
        int mid = l + r >> 1;
        if (dijkstra(mid))r = mid;
        else l = mid + 1;
    }
    if (r == 1e6 + 1)puts("-1");
    else cout << r << endl;
}


分层图思路:
对于可以将 k k k条边的权值变为免费的情况,或是每条边有 k k k种操作的题,我们可以对其建立一个 ( k + 1 ) (k+1) k+1层的新图来进行求解。
在这里插入图片描述
图太丑了hhh。
我们将黑色的边作为普通边,其他颜色的边都为权值为0的边

那么对于这个新图我们就可以对其进行最短路操作,如果选择了权值为0的点那么他就会进入下一层图,以此类推,所以说,最后我们的答案在最后一层图的终点,也就是dis[(k+1)*n]处。

而对于松弛操作,由于我们是求第k+1大的边,所以每次取最大值即可。

#include<iostream>
#include<algorithm>
#include<cstring>
#include<string>
#include<map>
#include<cstdio>
#include<cmath>
#include<iomanip>
#include<sstream>
#include<queue>
#define pi 3.1415926535
using namespace std;
typedef long long ll;
const int mod = 1e9 + 7;
typedef pair<int, int> pii;
const int N = 1e6 + 10;
int h[N], e[N], ne[N], idx, w[N];
void add(int a, int b, int c) {
	e[idx] = b, ne[idx] = h[a], w[idx] = c, h[a] = idx++;
}

int n, m, k;
int id[N];
int dis[N];
bool st[N];

int dijkstra() {
	memset(dis, 0x3f, sizeof dis);
	memset(st, false, sizeof st);
	priority_queue<pii, vector<pii>, greater<pii>> heap;
	heap.push({ 0,1 });
	dis[1] = 0;
	while (heap.size())
	{
		pii t = heap.top();
		heap.pop();
		int ver = t.second;
		if (st[ver])continue;
		st[ver] = true;
		for (int i = h[ver]; ~i; i = ne[i]) {
			int j = e[i];
			if (dis[j] > max(dis[ver], w[i])) {
				dis[j] = max(dis[ver], w[i]);
				heap.push({ dis[j],j });
			}
		}

	}
	return dis[(k+1)*n];
}

int main() {
	memset(h, -1, sizeof h);
	cin >> n >> m >> k;
	while (m--)
	{
		int a, b, c;
		cin >> a >> b >> c;
		add(a, b, c);
		add(b, a, c);
		for (int j = 1, z = 0; j <= k; j++) {
		//第j层和第j+1层图之间建边
			add(a + (j - 1) * n, b + j * n, z);//正向边
			add(b + (j - 1) * n, a + j * n, z);//反向边
		//第j+1层图建边
			add(a + j * n, b + j * n, c);
			add(b + j * n, a + j * n, c);
		}
	}
	if (dijkstra() == 0x3f3f3f3f)puts("-1");
	else cout << dijkstra() << endl;	
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值