• 博客园logo
  • 会员
  • 众包
  • 新闻
  • 博问
  • 闪存
  • 赞助商
  • HarmonyOS
  • Chat2DB
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录
jacklee404
Never Stop!
博客园    首页    新随笔    联系   管理    订阅  订阅
通信线路-二分+双端队列BFS

通信线路-二分+双端队列BFS

题目

通信线路

思路

​ 理解一下题意,找到一条路径使得,源点到终点的路径中第\(k + 1\)大的边权最小,也就是下图标注的\((1->2->5)\)的路径花费为\(4\).

image-20230412092441372

​ 求最大值最小显然我们可以想到二分答案,那么如何二分那,我们先设一个\(x\), 对于\(> x\) 的边权我们设置为\(1\), \(\le x\)的边权设置为\(0\), 那么最短路径含义就是最少经过多少条\(> x\)的边可以到达终点\(n\), 如果经过的点数\(\le k\)的话我们返回\(true\), 显然对于更大的\(x\)他是一定满足该特性的,对于更小的\(x\)他可能不满足该特性,所以是满足二段性的。

​ 对于搜到的\(x\),若其\(dist[x] < k\), 那么我们可以继续向左枚举直到\(0\), 作为边界,位置,所以我们一定可以枚举到\(dist[x] == k\)的点,另外我们在设置\(1e6 + 1\)作为右端点当作图不连通。

​ 假设不存在边界的情况下,搜到的结果一定是答案,因为我们假设\(res < k\), 那么一定存在更小的\(ans\)满足条件

image-20230412093345712

Code

#include <iostream>
#include <cstring>
#include <deque>

using i64 = long long;

const int N = 1e3 + 10, M = 2e4 + 10;

/*
	问题:
		对于任意的1~n的路径,求该路径上第k+1大的边权最小的方案
	
	二分:
		check(x) 从1~n的最少经过>x的路径边数不超过k个

	边界处理:
		如果从1到N不连通, 肯定会一直check(false) 我们让其搜到Max(L_i) + 1
		另外答案可能为0, 即不存在经过1~n超过k个的点,我们的ans会搜到0	

	复杂度
	O(m \times log(MAX(L_i)))
*/


std::deque<int> q1;

int h[N], w[M], e[M], ne[M], idx;

int dist[N];

bool st[N];

int n, p, k;

void add(int a, int b, int c) {
	ne[idx] = h[a], h[a] = idx, e[idx] = b, w[idx ++] = c;
}


bool check(int x) {
	memset(dist, 0x3f, sizeof dist);
	memset(st, false, sizeof st);
	
	q1.push_back(1);

	dist[1] = 0;

	while (q1.size()) {
		int t = q1.front(); q1.pop_front();

		if (st[t]) continue;
		st[t] = true;

		for (int i = h[t]; ~i; i = ne[i]) {
			int j = e[i], v = w[i] > x;
			if (dist[j] > dist[t] + v) {
				dist[j] = dist[t] + v;
				if (!v) {
					q1.push_front(j);
				} else q1.push_back(j);
			}	
		}
	}

	// std::cout << dist[n] << "\n";

	return dist[n] <= k;
}


int main() {
	memset(h, -1, sizeof h);

	std::cin >> n >> p >> k;

	for (int i = 1; i <= p; i ++) {
		int a, b, l;

		std::cin >> a >> b >> l;

		add(a, b, l);
		add(b, a, l);
	}

	int l = 0, r = 1e6 + 1;

	while (l < r) {
		int mid = l + r >> 1;

		if (check(mid)) r = mid;
		else l = mid + 1;
	}

	if (l == 1e6 + 1) {
		std::cout << -1;
	} else std::cout << l;
}
posted on 2023-04-12 09:45  Jack404  阅读(15)  评论(0)    收藏  举报
刷新页面返回顶部
博客园  ©  2004-2025
浙公网安备 33010602011771号 浙ICP备2021040463号-3