1. 程式人生 > >POJ 3662 Telephone Lines (二分+Dijkstra: 最小化第k大的值)

POJ 3662 Telephone Lines (二分+Dijkstra: 最小化第k大的值)

john tel 額外 距離 最優 cst print sig per

題意

Farmer John想從電話公司修一些電纜連接到他農場。已知N個電線桿編號為1,2,?N,其中1號已經連接電話公司,N號為農場,有P對電線桿可連接。

現給出P對電線桿距離Ai,Bi,Li表示Ai和Bi可連接,需要長度為Li的電纜。

電話公司贊助FJ K條免費電纜,額外的支出為剩下所需電纜的最大長度。求出最小費用。

思路

設mid為某條線的長度,長於mid的線放到免費額度裏,否則自己掏錢。如果存在一個臨界值x,使得長於x的電線數量恰好等於K,這個臨界值對應的解就是最優解。如何計算長於mid的電線數量呢?排序比較肯定不行的,因為不知道這條電線用不用得上。所以需要Dijkstra,又因為在mid固定的條件下,電線的花費只取決於長度是否大於mid,所以可以將大於的取為1,小於的取為0。這樣花費就可以計算了,並且花費等於長於mid的電線數量,也就是需要自己掏錢的電線的數量。

#include <iostream>
#include <cstdio>
#include <vector>
#include <queue>
#include <cstring>
using namespace std;
int N, P, K;
struct  edge { // 頂點屬性
	int to, length;
	edge(int to, int length) : to(to), length(length) {}
	bool operator<(const edge &b) const {
		return length > b.length;
	}
};
vector<edge> G[1005];
int d[1005];
bool dijkstra_C(int s, int x) { // 求出長度>x的最少個數(>x邊權為1,否則為0,求最短路),判斷x是否免費,當二分判定出第一個不免費的x,就是所求的最小的最大x。
	priority_queue<edge> que;
	memset(d, 0x3f, sizeof(d));
	d[s] = 0;
	que.push(edge(s, 0));
	while (!que.empty()) {
		edge p = que.top(); que.pop();
		int v = p.to;
		if (d[v] < p.length) continue;
		for (unsigned int i = 0; i<G[v].size(); ++i) {
			edge e = G[v][i];
			int len = e.length > x;
			if (d[e.to] > d[v] + len) {
				d[e.to] = d[v] + len;
				que.push(edge(e.to, d[e.to]));
			}
		}
	}
	return d[N] <= K;
}
void solve() {
	dijkstra_C(1, 1);
	if (d[N] == 0x3f3f3f3f) {
		printf("-1\n");
		return;
	}
	int lb = -1, ub = 1000000 + 5; // (lb, ub)
	while (ub - lb > 1) {
		int mid = (lb + ub) >> 1;
		if (dijkstra_C(1, mid)) ub = mid; // (lb, ub]
		else lb = mid;
	}
	printf("%d\n", ub);
}
int main() {
	scanf("%d%d%d", &N, &P, &K);
	int a, b, l;
	for (int i = 0; i < P; ++i) {
		scanf("%d%d%d", &a, &b, &l);
		G[a].push_back(edge(b, l));
		G[b].push_back(edge(a, l));
	}
	solve();
	return 0;
}

POJ 3662 Telephone Lines (二分+Dijkstra: 最小化第k大的值)