【二分答案+dijkstra】P1948 [USACO08JAN]Telephone Lines S

P1948 [USACO08JAN]Telephone Lines S

#include
#include
#include
#include
#include
#include
#include
#include 
#define  LL long long
using namespace std;
const int N = 100010;
int head[1010], cnt = 0, dis[1010], vis[1010];
int k, n, m, mx;
//二分答案+dijkstra
//因此,我们可以二分最大的花费x,x属于[l, r], (l为最小花费,r为最大花费), x然后将大于x的边看做权值为1的边,将小于等于x的边看做权值为零的边,
//然后找到从点1到点n的最短路,若最短路的长度大于k,则要连接的对数大于k对,在[x + 1, r]中继续二份查找;若最短路的长度小于k,
//则要连接的对数比k小,在[l, x]中继续二份查找,最终,l即为所求。
struct node {
	int nxt;
	int val;
	int to;
}e[N];
struct priority//运用优先队列(堆)来实现优化
{
	long long ans;//存储可用的ans值
	int id;
	bool operator  <(const priority& x) const//重载<号,使其可以使用优先队列
	{
		return x.ans < ans;
	}
};
priority_queue  q;
inline void add(int u, int v, int w) {
	e[++cnt].to = v;
	e[cnt].val = w;
	e[cnt].nxt = head[u];
	head[u] = cnt;
}

bool dijkstra(int s, int mid, int k) {
	int u;
	memset(dis, 0x3f, sizeof(dis));
	memset(vis, 0, sizeof(vis));
	dis[1] = 0;
	q.push({ 0,1 });
	while (!q.empty()) {
		priority t = q.top();
		u = t.id;
		q.pop();
		if (!vis[u]) {
			vis[u] = 1;
			for (int i = head[u]; i; i = e[i].nxt) {
				int v = e[i].to, w = (e[i].val > mid ? 1 : 0);//判断是否大于免费费用,如果大于,则免费,同时w记为1,表示增加一条免费边
				if (dis[v] > dis[u] + w) {
					dis[v] = dis[u] + w;
					if (!vis[v]) {//防止重复入队
						q.push({ dis[v], v });
					}
				}
			}

		}
	}
	return dis[n] <= k;

}
//
int main() {
	ios::sync_with_stdio(false);
	cin.tie(0);
	int flag = 0, l, r, mid;
	cin >> n >> m >> k;//n个电线杆,m条电话线,k条免费的电话线.
	for (int i = 1, x, y, val; i <= m; i++) {
		cin >> x >> y >> val;//x,y电线之间的费用val
		add(x, y, val); add(y, x, val);//无向边
		mx = max(mx, val);//找到最大费用
	}
	l = 0, r = mx;
	while (l < r) {//二分费用,大于mid的费用的线段免费,查找免费数个数,如果大于k则增大mid,如果小于k则左边二分,mid减小
		mid = l + ((r - l) >> 2);
		if (dijkstra(1, mid, k)) {
			r = mid;
			flag = 1;
		}
		else {
			l = mid + 1;
		}
	}
	if (flag == 1)
		cout << l;
	else
		cout << "-1";
	return 0;

}

你可能感兴趣的:(算法学习,拓扑学,蓝桥杯,算法)