洛谷 P1948. Telephone Lines S
问题描述
在无向图上求出一条从
分析
题目要求从
关键转化:把"最多免费
思路一:分层图 + DP(Dijkstra)
把"已用掉的免费次数"作为状态的第二维:设
状态转移(对边
- 付费:
,即新的最大花费取"历史最大值"与这条边权值中的较大者; - 免费(当
): ,最大花费保持不变。
把二元组
最终答案取
复杂度:状态数
思路二:二分答案 + 01 最短路
注意到答案具有单调性:如果"支付上限为
是否存在一条从
到 的路径,使得路径上权值大于 的边不超过 条?
判定时把边权"降维":权值大于
关于二分边界:答案不可能超过最大边权,取
复杂度:二分
参考代码
思路一:分层图 + DP 实现
#include <bits/stdc++.h>
using namespace std;
typedef pair<int, pair<int, int>> PIII;
const int N = 1010, M = 20010;
int h[N], e[M], w[M], ne[M], idx = 1;
int vis[N][10010];
int d[N][10010];
int n, m, k;
void add(int a, int b, int c) {
e[idx] = b;
w[idx] = c;
ne[idx] = h[a];
h[a] = idx++;
}
int dijkstra() {
memset(d, 0x3f, sizeof d);
d[1][0] = 0;
priority_queue<PIII, vector<PIII>, greater<PIII>> heap;
heap.push({d[1][0], {1, 0}});
while (heap.size()) {
auto t = heap.top();
heap.pop();
int x = t.second.first, p = t.second.second, dis = t.first;
if (vis[x][p]) continue;
vis[x][p] = 1;
for (int i = h[x]; i; i = ne[i]) {
int y = e[i];
if (d[y][p] > max(dis, w[i])) {
d[y][p] = max(dis, w[i]);
heap.push({d[y][p], {y, p}});
}
if (p < k && d[y][p + 1] > dis) {
d[y][p + 1] = dis;
heap.push({d[y][p + 1], {y, p + 1}});
}
}
}
int ans = 0x3f3f3f3f;
for (int p = 0; p <= k; p++) ans = min(ans, d[n][p]);
return ans == 0x3f3f3f3f ? -1 : ans;
}
int main() {
cin >> n >> m >> k;
for (int i = 1; i <= m; i++) {
int a, b, c; cin >> a >> b >> c;
add(a, b, c);
add(b, a, c);
}
cout << dijkstra() << endl;
return 0;
}思路二:二分
#include <bits/stdc++.h>
using namespace std;
const int N = 1010, M = 20010;
int head[N], e[M], w[M], ne[M], idx;
int dist[N], vis[N];
int n, p, k;
void add(int a, int b, int c) {
e[idx] = b;
w[idx] = c;
ne[idx] = head[a];
head[a] = idx++;
}
bool check(int mid) {
memset(dist, 0x3f, sizeof dist);
memset(vis, false, sizeof vis);
priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> q;
dist[1] = 0;
q.push({dist[1], 1});
while (q.size()) {
auto t = q.top(); q.pop();
int x = t.second;
if (vis[x]) continue;
vis[x] = true;
for (int i = head[x]; i != -1; i = ne[i]) {
int y = e[i], v = w[i] > mid ? 1 : 0;
if (dist[y] > dist[x] + v) {
dist[y] = dist[x] + v;
q.push({dist[y], y});
}
}
}
return dist[n] <= k;
}
int main() {
memset(head, -1, sizeof head);
cin >> n >> p >> k;
for (int i = 0; i < p; i++) {
int a, b, c; cin >> a >> b >> c;
add(a, b, c);
add(b, a, c);
}
int l = 0, r = 1000001;
while (l < r) {
int mid = l + r >> 1;
if (check(mid)) r = mid;
else l = mid + 1;
}
if (r != 1000001) cout << r << endl;
else cout << -1 << endl;
return 0;
}