原题来自:USACO 2008 Jan. Silver
在郊区有 N N N 座通信基站, P P P 条双向电缆,第 i i i 条电缆连接基站 A i A_i Ai 和 B i B_i Bi。特别地, 1 1 1 号基站是通信公司的总站, N N N 号基站位于一座农场中。现在,农场主希望对通信线路进行升级,其中升级第 i i i 条电缆需要花费 L i L_i Li。
电话公司正在举行优惠活动。农场主可以指定一条从 1 1 1 号基站到 N N N 号基站的路径,并指定路径上不超过 K K K 条电缆,由电话公司免费提供升级服务。农场主只需要支付在该路径上剩余的电缆中,升级价格最贵的那条电缆的花费即可。求至少用多少钱能完成升级。
一句话题意,在加权无向图上求出一条从 1 1 1 号结点到 N N N 号结点的路径,使路径上第 K + 1 K + 1 K+1 大的边权尽量小。
第一行三个整数 N , P , K N, P, K N,P,K.
接下来 P P P 行,每行三个整数 A i , B i , L i A_i, B_i, L_i Ai,Bi,Li.
若不存在从 1 1 1 到 N N 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 K K 大的最小?显然这还是二分。
我们可以直接二分答案。然后每次 c h e c k check check 答案的时候,将所有的大于这个值的边的权值改为 1 1 1,其余的改为 0 0 0。其实就是标记了一下比当前枚举答案大的。
然后跑最短路,如果最短路跑出来小于 K K K 则说明满足一条路径上二分的这个答案是第 K + 1 K + 1 K+1 大的,反之则说明当前枚举的答案不是可行解。继续二分。
接下来我们看看详细复杂,啰嗦,累赘的代码。
#include
#include
#include
#include
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\n", 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\n", Find());
return 0;
}