【2019 暑假JSOI Day 2 T2】小说(二分+最短路)

3 篇文章 0 订阅
1 篇文章 0 订阅

题目

【题目背景】
由于小 X 是一位奆老, 奆老总是忙得一刻也停不下来。 他刚刚准备完食物, 小 X 童年的
挚友小 S 和小 Z 来找他帮忙了……
【题目描述】
小 S 和小 Z 十分喜欢看网络写手“ 2 5 2^5 25” 的小说, 但由于需要付费才能阅读, 而小 S 和小
Z 的零花钱有非常少, 他们只能找小 X 靠黑科技侵入给网站, 把小说给他们。
然而小 X 又非常的爱慕虚荣, 他要小 S 和小 Z 到自己家里来取小说。
小 S、 小 Z 和小 X 都居住在扬中市, 扬中市共有 n n n 个小区, m m m 条主干道(假设每条主干
道都是双行线) 。 小 S 家住在 1 1 1 号小区, 小 X 家住在 n n n 号小区。 小 S 每经过一条主干道需要
耗费 z 点体力, 但由于小 S 的人脉非常广, 每当他到达一个小区, 他都会和好友攀谈直到体
力回满。
由于小 Z 也希望能看到小说, 所以他答应帮助小 S k k k 次, 这 k k k 次小 S 经过主干道不需要
耗费体力。
由于小 S 生性懒惰, 他希望耗费最少的体力到达小 X 家, 请问他最少耗费多少体力?
注意: 如果小 S 到小 X 家可以一路上都由小 Z 背着, 那么体力上限为 0 0 0
如果小 S 到不了小 X 家, 小 S 会很伤心, 体力上限为 − 1 -1 1

【输入格式】
第 1 行三个整数 n , m , k n,m,k n,m,k, 意思如题目描述。
第 2 到第 n+1 行是 x , y , z x,y,z x,y,z 指走连接 x x x 号小区和 y y y 号小区的主干道要耗费 z z z 点体力

【输出格式】
一行一个整数, 表示小 S 最少耗费的体力。

【输入样例】
5 7 1
1 2 5
3 1 4
2 4 8
3 2 3
5 2 9
3 4 7
4 5 6

【输出样例】
4

【样例解释】
小 S 的行走路线: 1 → 3 → 2 → 5 1\rightarrow 3\rightarrow 2\rightarrow 5 1325, 其中 2 → 5 2\rightarrow 5 25 这条主干道由小 Z 帮助小 S 通过。

【数据范围】
对于 30%的数据: n < = 20 ; m < = 100 ; n<=20;m<=100; n<=20;m<=100;
对于 60%的数据: n < = 100 ; m < = 1000 ; n<=100;m<=1000; n<=100;m<=1000;
对于 100%的数据: n < = 1000 ; m < = 10000 ; z < = 1000000 ; n<=1000;m<=10000;z<=1000000; n<=1000;m<=10000;z<=1000000;

【后记】
附上两句 2 5 2^5 25 的语录:
1.扔掉一件东西的时候, 其实扔掉的不止是东西, 记下一件事情的时候, 其实记下的不止是
事情。
2.记在脑海里的东西或许会被忘却, 但记在心里的东西, 是不会消逝的。

思路

这一题我过了(Day 2终于拿了一次第一
答案显然具有单调性,所以可以进行二分答案。
于是,我们可以将问题转化为:有没有一种方法,使体力上限不超过mid。
check函数很好写,只需要把体力 > mid 的路径看做1,把 < mid 的路径看做0,dijkstra + 堆优化 即可(我也不知道要不要加优化,反正我加了)。

代码

#include <bits/stdc++.h>
using namespace std;
const int M = 4010, N = 40100, Z = 1000000;
int n, m, k, dis[M], vis[M], s[M], t[N], e[N], l[N];
struct node {
	int id, dis;
	friend bool operator < (node a, node b) {return a.dis > b.dis;}
}; priority_queue <node> q;

int dijk(int x) {
	memset(vis, 0, sizeof(vis));
	memset(dis, 0x3f3f3f3f, sizeof(dis));
	node f = (node){1, 0};
	dis[1] = 0; q.push(f);
	while (!q.empty()) {
		f = q.top(); q.pop();
		vis[f.id] = 1;
		for (int i = s[f.id]; i != EOF; i = t[i]) {
			int e2 = e[i], k2 = dis[f.id];
			if (vis[e2]) continue;
			if (l[i] >= x) k2++;
			if (dis[e2] > k2) {
				dis[e2] = k2;
				node tmp = (node) {e2, dis[e2]};
				q.push(tmp);
			}
		}
	}
	return dis[n] >= k+1;
}

int main() {
	cin >> n >> m >> k;
	memset(s, -1, sizeof(s));
	int tot = 1, u, v, w;
	for (int i = 1; i <= m; i++) {
		cin >> u >> v >> w;
		t[tot] = s[u], s[u] = tot, e[tot] = v, l[tot] = w, tot++;
		t[tot] = s[v], s[v] = tot, e[tot] = u, l[tot] = w, tot++;
	}
	int l = 0, r = Z+100, ans = -1;
	while (l <= r) {
		int m = (l+r) >> 1;
		if (dijk(m)) l = m+1, ans = m;
		else r = m-1;
	}
	if (ans > Z) cout << -1 << endl;
	else if (r < 0) cout << 0 << endl;
	else cout << ans << endl;
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值