题目描述
原题来自:USACO 2008 Jan. Silver
在郊区有 (N) 座通信基站,(P) 条双向电缆,第 (i) 条电缆连接基站 (A_i) 和 (B_i)。特别地,(1) 号基站是通信公司的总站,(N) 号基站位于一座农场中。现在,农场主希望对通信线路进行升级,其中升级第 (i) 条电缆需要花费 (L_i)。
电话公司正在举行优惠活动。农场主可以指定一条从 (1) 号基站到 (N) 号基站的路径,并指定路径上不超过 (K) 条电缆,由电话公司免费提供升级服务。农场主只需要支付在该路径上剩余的电缆中,升级价格最贵的那条电缆的花费即可。求至少用多少钱能完成升级。
一句话题意,在加权无向图上求出一条从 (1) 号结点到 (N) 号结点的路径,使路径上第 (K + 1) 大的边权尽量小。
输入格式
第一行三个整数 (N, P, K).
接下来 (P) 行,每行三个整数 (A_i, B_i, L_i).
输出格式
若不存在从 (1) 到 (N) 的路径,输出 -1。否则输出所需最小费用。
样例输入
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
分析
看到最大的最小?那一定是二分。看到第 (K) 大的最小?显然这还是二分。
我们可以直接二分答案。然后每次 (check) 答案的时候,将所有的大于这个值的边的权值改为 (1),其余的改为 (0)。其实就是标记了一下比当前枚举答案大的。
然后跑最短路,如果最短路跑出来小于 (K) 则说明满足一条路径上二分的这个答案是第 (K + 1) 大的,反之则说明当前枚举的答案不是可行解。继续二分。
接下来我们看看详细复杂,啰嗦,累赘的代码。
AC代码
#include <cstdio>
#include <vector>
#include <cstring>
#include <algorithm>
using namespace std;
int n, p, k;
struct edge { // 结构体 边
int v, cost;
edge() {}
edge(int V, int Cost) {
v = V;
cost = Cost;
}
};
const int MAXN = 2005;
const int INF = 0x3f3f3f3f;
vector<edge> graph[MAXN];
int ma = 0;
bool flag = true;
int dist[MAXN];
bool vis[MAXN];
vector<edge> mp[MAXN];
void read(int &x) { // 读优
int k = 1;
x = 0;
char s = getchar();
while (s < '0' || s > '9') {
if (s == '-')
k = -1;
s = getchar();
}
while (s >= '0' && s <= '9') {
x = (x << 1) + (x << 3) + (s - '0');
s = getchar();
}
x *= k;
return;
}
void Add_Edge(int u, int v, int cost) { // 存边
graph[u].push_back(edge(v, cost));
graph[v].push_back(edge(u, cost));
}
void init(int x) { // 初始化
for (int i = 1; i <= n; i++) mp[i].clear();
memset(dist, 0x3f, sizeof dist);
memset(vis, 0, sizeof vis);
for (int i = 1; i <= n; i++)
for (int j = 0; j < graph[i].size(); j++) {
int V = graph[i][j].v;
int Cost = graph[i][j].cost;
if (Cost > x) { // 跑最短路时的"01"图
mp[i].push_back(edge(V, 1));
mp[V].push_back(edge(i, 1));
} else {
mp[i].push_back(edge(V, 0));
mp[V].push_back(edge(i, 0));
}
}
// for(int i = 1; i <= n; i++)
// for(int j = 0; j < mp[i].size(); j++)
// printf("%d %d %d
", i, mp[i][j].v, mp[i][j].cost);
return;
}
int dijkstra(int x) { // dijkstra
init(x);
dist[1] = 0;
for (int i = 1; i <= n; i++) {
int k = 0, mi = INF;
for (int j = 1; j <= n; j++)
if (!vis[j] && dist[j] < mi) {
mi = dist[j];
k = j;
}
vis[k] = true;
for (int j = 0; j < mp[k].size(); j++) {
int V = mp[k][j].v, Cost = mp[k][j].cost;
if (dist[k] + Cost < dist[V])
dist[V] = dist[k] + Cost;
}
}
return dist[n];
}
bool check(int mid) { // 二分 判断可行性函数
int t = dijkstra(mid);
if (t == INF) {
flag = false;
return false;
}
if (t > k)
return false;
return true;
}
int Find() { // 二分
int l = 0, r = ma;
while (l < r) {
int mid = (l + r) >> 1;
if (check(mid))
r = mid;
else {
if (flag == false)
return -1;
l = mid + 1;
}
}
return l;
}
int main() {
read(n); read(p); read(k);
for (int i = 1; i <= p; i++) {
int u, v, cost;
read(u); read(v); read(cost);
Add_Edge(u, v, cost);
ma = max(ma, cost);
}
printf("%d
", Find());
return 0;
}