题目内容
塔子哥很喜欢旅游,目标是游遍全球。作为一个程序猿,塔子哥想写个程序来规划自己的旅游路线。
现在塔子哥来到了一个城市,这个城市有 n n n 个景点,有 m m m 条路连通这 n n n 个景点。游览景点 i i i 花费的时间为 t i t_i ti ,获得的快乐值为 h i h_i hi 。
第 i i i 条路连通 u i u_i ui 和 v i v_i vi 这两个景点( u i ≠ v i u_i\neq v_i ui=vi),通过这条路花费的时间为 w i w_i wi 。
塔子哥精力有限,所以至多只会游览 3 3 3 个景点,且这 3 3 3 个景点是相邻的,一天游览和交通所需的时间不能超过 k k k 。
现在,塔子哥想问你,在给定的时间下,通过游览景点可以获得的最大快乐值是多少。
输入描述
第一行,输入三个整数 n , m , k ( 1 ≤ n , m ≤ 1 0 5 , 1 ≤ k ≤ 1 0 9 ) n, m, k(1\leq n, m\leq 10^5, 1\leq k\leq 10^9) n,m,k(1≤n,m≤105,1≤k≤109),表示景点数,路径数和最大交通时间。
第二行输入 n n n 个整数,表示每个景点的快乐值 h i ( 1 ≤ h i ≤ 1 0 9 ) h_i(1\leq h_i\leq 10^9) hi(1≤hi≤109)
第三行输入 n n n 个整数,表示游览每个景点的时间 t i ( 1 ≤ t i ≤ 1 0 9 ) t_i(1\leq t_i\leq 10^9) ti(1≤ti≤109)
接下来 m m m 行,第 i i i 行三个数 u i , v i , w i ( 1 ≤ u i , v i ≤ n , 1 ≤ w i ≤ 1 0 9 ) u_i, v_i, w_i(1\leq u_i, v_i\leq n, 1\leq w_i\leq 10^9) ui,vi,wi(1≤ui,vi≤n,1≤wi≤109) 表示两个景点和通过这条路径花费的时间。
输出描述
一个整数,表示在给定的时间下,通过游览景点可以获得的最大快乐值。
样例
输入
4 3 10
10 9 8 7
1 2 3 4
1 2 1
2 3 1
3 4 10
输出
27
思路:枚举 + 二分
这题至多游览三个景点,而且要求任意两个游览的景点,至多通过两条路径可以到达。
考虑游览的景点数:
- 一个,遍历所有景点即可
- 两个,枚举每条边即可
- 三个,枚举每个点 x x x 的所有邻居,根据通过路径的时间和游览景点的时间从小到大排序。然后继续枚举点 x x x 的邻居 y y y ,再二分得到符合条件的邻居 z z z 即可。
时间复杂度: O ( m log m ) O(m\log m) O(mlogm)
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<ll, ll> PLL;
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n, m, k;
cin >> n >> m >> k;
vector<ll> h(n), t(n);
for (int i = 0; i < n; ++i) cin >> h[i];
for (int i = 0; i < n; ++i) cin >> t[i];
// 建图
vector<vector<PLL>> g(n);
for (int i = 0; i < m; ++i) {
int u, v;
ll w;
cin >> u >> v >> w;
u--; v--;
g[u].push_back({v, w});
g[v].push_back({u, w});
}
ll ans = 0;
// 一个点
for (int i = 0; i < n; ++i) {
if (t[i] <= k) {
ans = max(ans, h[i]);
}
}
// 两个点
for (int i = 0; i < n; ++i) {
for (auto& [j, w]: g[i]) {
if (w + t[i] + t[j] <= k) {
ans = max(ans, h[i] + h[j]);
}
}
}
// 三个点
for (int i = 0; i < n; ++i) {
vector<PLL> vec;
for (auto& [j, w]: g[i]) {
// 选择每个点的时间为 w + t[j],获得的快乐值为 h[j]
vec.emplace_back(w + t[j], h[j]);
}
// 按照选择每个点需要耗费的时间为第一关键字排序
sort(vec.begin(), vec.end());
// 第 i 个点作为中间点,再选择其两个邻居景点
// 此时,我们可以枚举第 x 个点作为第一个邻居点,1 到 x - 1 中选择一个合适的点作为第二个邻居点
// 我们对于时间先排序,那么我们可以知道的是,从 1 到 x - 1 中,二分出一个总时间小于等于 k ,价值尽可能大的第二邻居点
for (int x = 1; x < int(vec.size()); ++x) {
int l = 0, r = x - 1;
while (l < r) {
int mid = (l + r + 1) >> 1;
if (vec[mid].first + vec[x].first + t[i] <= k) {
l = mid;
} else {
r = mid - 1;
}
}
if (vec[l].first + vec[x].first + t[i] <= k) {
ans = max(ans, h[i] + vec[x].second + vec[l].second);
}
vec[x].second = max(vec[x].second, vec[x - 1].second);
}
}
cout << ans << "\n";
return 0;
}