题意
给出一个图,求出其中一条从点 1 1 1到点 N N N的路径,在上面选掉 K K K条边,使得剩下的边中最大的最小。
思路
这个题其实是求出一条 1 1 1到 N N N的路径中使得第 K + 1 K+1 K+1的边最小。
解法一:
首先看到字样“最大的最小”,我们可以感觉这个题可以用二分,然后细读一下发现答案满足单调性。
当我们二分出一个
m
i
d
mid
mid,可以求出这个图中从
1
1
1到
N
N
N的路径中大于
m
i
d
mid
mid的边的个数,如果个数超过了
K
K
K,说明答案不可行。
求出这个边的个数我们只用把超过
m
i
d
mid
mid的边权记为
1
1
1,然后利用
S
P
F
A
SPFA
SPFA或双端队列
B
F
S
BFS
BFS的方法就可以求出了。
解法二:
可以用动态规划,设
f
[
u
]
[
k
]
f[u][k]
f[u][k]为从
1
1
1到
u
u
u中已经选了
k
k
k条路的最优答案,可得:
表示选
f
[
v
]
[
k
+
1
]
=
m
i
n
(
f
[
v
]
[
k
+
1
]
,
f
[
u
]
[
k
]
)
f[v][k+1]=min(f[v][k+1],f[u][k])
f[v][k+1]=min(f[v][k+1],f[u][k])
表示不选
f
[
v
]
[
k
]
=
m
i
n
(
f
[
v
]
[
k
]
,
m
a
x
(
f
[
u
]
[
k
]
,
e
.
v
)
f[v][k]=min(f[v][k],max(f[u][k],e.v)
f[v][k]=min(f[v][k],max(f[u][k],e.v)
其中
v
v
v表示边的另一个点,
e
.
v
e.v
e.v表示权值。
因为没有一个确定的顺序
d
p
dp
dp,我们可以用
S
P
F
A
SPFA
SPFA来转移,相当于取
m
i
n
min
min就为松弛操作。
代码
解法一:
#include<queue>
#include<cstdio>
#include<cstring>
using namespace std;
int N, P, K, tot, l, r;
int head[1001], vis[1001], d[1001];
struct node{
int to, v, next;
}e[20001];
void add(int x, int y, int z) {
e[++tot].to = y;
e[tot].v = z;
e[tot].next = head[x];
head[x] = tot;
e[++tot].to = x;
e[tot].v = z;
e[tot].next = head[y];
head[y] = tot;
}
int check(int x) {
deque<int> q;
memset(d, 0, sizeof(d));
memset(vis, 0, sizeof(vis));
q.push_back(1);
vis[1] = 1;
while (q.size()) {
int u = q.front(), v;
q.pop_front();
for (int i = head[u]; i; i = e[i].next) {
v = e[i].to;
if (!vis[v] || d[v] >= d[u] + 1) {
vis[v] = 1;
//双端队列做法可保证每次取出来的点都是最优的
if (e[i].v > x) q.push_back(v), d[v] = d[u] + 1;
else q.push_front(v), d[v] = d[u];
}
}
}
return d[N] <= K && vis[N];
}
int main() {
scanf("%d %d %d", &N, &P, &K);
for (int i = 1, x, y, z; i <= P; i++) {
scanf("%d %d %d", &x, &y, &z);
add(x, y, z);
r = max(r, z);
}
l = 1;
int mid, f = 0;//f记录无解情况
while (l < r) {
mid = (l + r) >> 1;
if (check(mid)) r = mid, f = 1;
else l = mid + 1;
}
printf("%d", f ? r : -1);
}
解法二:
#include<queue>
#include<cstdio>
#include<cstring>
using namespace std;
int N, P, K, tot;
int head[1001], f[1001][1001], v[1001][1001];
struct node{
int to, v, next;
}e[20001];
void add(int x, int y, int z) {
e[++tot].to = y;
e[tot].v = z;
e[tot].next = head[x];
head[x] = tot;
e[++tot].to = x;
e[tot].v = z;
e[tot].next = head[y];
head[y] = tot;
}
void spfa() {
memset(f, 127 / 3, sizeof(f));
queue< pair<int, int> > q;
q.push(make_pair(1, 0));
v[1][0] = 1;
f[1][0] = 0;
pair<int, int> a;
while (q.size()) {
a = q.front();
q.pop();
v[a.first][a.second] = 0;
for (int i = head[a.first]; i; i = e[i].next) {
if (a.second < K && f[a.first][a.second] < f[e[i].to][a.second + 1]) {//use
f[e[i].to][a.second + 1] = f[a.first][a.second];
if (!v[e[i].to][a.second + 1]) {
v[e[i].to][a.second + 1] = 1;
q.push(make_pair(e[i].to, a.second + 1));
}
}
if (max(e[i].v, f[a.first][a.second]) < f[e[i].to][a.second]) {//not use
f[e[i].to][a.second] = max(e[i].v, f[a.first][a.second]);
if (!v[e[i].to][a.second]) {
v[e[i].to][a.second] = 1;
q.push(make_pair(e[i].to, a.second));
}
}
}
}
}
int main() {
scanf("%d %d %d", &N, &P, &K);
for (int i = 1, x, y, z; i <= P; i++) {
scanf("%d %d %d", &x, &y, &z);
add(x, y, z);
}
spfa();
printf("%d", f[N][K] == 707406378 ? -1 : f[N][K]);
}