Telephone Lines POJ-3662(双端队列+二分)

一、内容

Farmer John wants to set up a telephone line at his farm. Unfortunately, the phone company is uncooperative, so he needs to pay for some of the cables required to connect his farm to the phone system.

There are N (1 ≤ N ≤ 1,000) forlorn telephone poles conveniently numbered 1..N that are scattered around Farmer John's property; no cables connect any them. A total of P (1 ≤ P ≤ 10,000) pairs of poles can be connected by a cable; the rest are too far apart.

The i-th cable can connect the two distinct poles Ai and Bi, with length Li (1 ≤ Li ≤ 1,000,000) units if used. The input data set never names any {Ai, Bi} pair more than once. Pole 1 is already connected to the phone system, and pole N is at the farm. Poles 1 and N need to be connected by a path of cables; the rest of the poles might be used or might not be used.

As it turns out, the phone company is willing to provide Farmer John with K (0 ≤ K < N) lengths of cable for free. Beyond that he will have to pay a price equal to the length of the longest remaining cable he requires (each pair of poles is connected with a separate cable), or 0 if he does not need any additional cables.

Determine the minimum amount that Farmer John must pay.

Input

* Line 1: Three space-separated integers: N, P, and K
* Lines 2..P+1: Line i+1 contains the three space-separated integers: Ai, Bi, and Li

Output

* Line 1: A single integer, the minimum amount Farmer John can pay. If it is impossible to connect the farm to the phone company, print -1.

Sample Input

5 7 1
1 2 5
3 1 4
2 4 8
3 2 3
5 2 9
3 4 7
4 5 6

Sample Output

4

二、思路

  • 根据题意我们需要寻找的是某段路径中第K+1大值最小。我们可以通过二分来求最大值最小这种问题。
  • ok函数:若经过最少的边数中权值大于mid的边数 <= k 返回真。
  • 我们将权值大于mid的边权当做1,小于等于mid的边当做0。 那么就转化为边权只有01的图中找寻最短路径。这里我们可以使用双端队列来求解。即若边权为0就放在队头,边权为1就放队尾。这样就保证遍历该点一次的时候就能获得最短路径。
  • 区间是[0, 1e6 + 1]。 因为若边数小于等于k就能走到n,那么需要花费0就可以了。 而如果图不联通,不能走到终点的话,那么必然x是1e6 + 1。 因为边最大是1e6,所以结果可能是1e6。所以多加上一个1来代表不连通。

三、代码

#include <cstdio>
#include <cstring>
#include <deque>
using namespace std; 
const int N = 1e3 + 5, M = 2e4 + 5;
struct E {
	int v, w, next;
} e[M];
int n, m, k, len = 1, head[N], d[N], u, v, w;
bool vis[N];
void add(int u, int v, int w) {
	e[len].v = v;
	e[len].w = w;
	e[len].next = head[u];
	head[u] = len++;
}
bool ok(int x) {
	//由于边权只有0或者1 所以用双端队列求解出最短距离 
	memset(vis, false, sizeof(vis)); 
	memset(d, 0x3f, sizeof(d));
	d[1] = 0;
	deque<int> q;
	q.push_back(1);
	while (!q.empty()) {
		int u = q.front();
		q.pop_front();
		if (vis[u]) continue;
		vis[u] = true;
		for (int j = head[u]; j; j = e[j].next) {
			int v = e[j].v;
			int w = e[j].w > x;
			if (!vis[v] && d[v] > d[u] + w) {
				d[v] = d[u] + w;
				if (w) q.push_back(v);
				else q.push_front(v); //若为0的边就放对头 否则放队尾 
			}
		}
	} 
	//判断走过大于x的边的边数是否小于等于k 
	return d[n] <= k; 
}
int main() {
	scanf("%d%d%d", &n, &m, &k);
	for (int i = 1; i <= m; i++) {
		scanf("%d%d%d", &u, &v, &w);
		add(u, v, w);
		add(v, u, w);
	}
	//二分[0, 1e6 + 1]最少经过的边的权值大于mid的最少边数是否小于等于k 
	int left = 0, r = 1e6 + 1;
	while (left < r) {
		int mid = (left + r) >> 1;
		if (ok(mid)) {
			r = mid;
		} else {
			left = mid + 1;
		}
	}
	if (r == 1e6 + 1) r = -1;
	printf("%d", r);
	return 0;
} 
发布了343 篇原创文章 · 获赞 244 · 访问量 4万+

猜你喜欢

转载自blog.csdn.net/qq_41280600/article/details/103882657