USACO 2008 Jan. Silver/loj10074架设电话线
第一步当然是分类:
$1.$有一条从$1$到$n$的路径且路径经过的边的数量小于等于$k$,那么最小费用为$0$。
判断方法:将所有边的边权设为$1$,进行并BFS,时间复杂度为$O(n)$。
$2.$若所有从$1$到$n$的路径经过的边数均大于$k$,则求出所有路径上第$k+1$大的边的最小值。
暴力做法明显会超时,注意到求第$k+1$大的边的最小值,似乎可以二分。
judge函数的实现:
若此时$(l+r)/2=ans$,我们要做的就是判断是否有一条从$1$到$n$的路径的第$k+1$条边小于等于$ans$。
转化$\rightarrow$将所有边权小于$ans$的边的边权设为$0$,所有大于等于$ans$的边的边权设为$1$,检验更新边权后从$1$到$n$的最短路的长度是否小于等于$k$,是,则证明所有大于$ans$的数都可行,$r=ans$,否,则证明所有小于$ans$的数都不可行,$l=ans+1$。
如何求更新边权后的图的最短路:
注意到图的边权只有$0和1$,可以使用相比其它最短路算法更为高效的01BFS。
01BFS:
使用双端队列($deque$),若可以进行松弛操作且此时边权为$0$,进行松弛操作并将边指向的点加入队列头部,若可以进行松弛操作且此时边权为$1$,进行松弛操作并将边指向的点加入队列尾部。
$Code:$
#include <iostream> #include <cstdio> #include <algorithm> #include <queue> #include <cstring> using namespace std; const int MAXN = 1010; int n, p, k, l = 0x7fffffff, r; deque<int> q; int head[MAXN], nxt[MAXN << 2], t[MAXN << 2], w[MAXN << 2], cnt; int step[MAXN]; bool vis[MAXN]; bool have = true; bool BFS() { q.push_back(1); step[1] = 1; while (!q.empty()) { int u = q.front(); if (u == n) { while (!q.empty()) q.pop_front(); if (step[u] <= k + 1) return true; return false; } for (int i = head[u]; i; i = nxt[i]) { if (!step[t[i]]) { step[t[i]] = step[u] + 1; q.push_back(t[i]); } } q.pop_front(); } have = false; return false; } bool judge(int cur) { memset(step, 0, sizeof(step)); memset(vis, 0, sizeof(vis)); q.push_back(1); vis[1] = true; for (int i = 1; i <= n; i++) { step[i] = 0x7fffffff; } step[1] = 0; while (!q.empty()) { int u = q.front(); q.pop_front(); if (u == n) { while (!q.empty()) q.pop_front(); if (step[u] <= k) return true; return false; } for (int i = head[u]; i; i = nxt[i]) { int len; if (w[i] <= cur) len = 0; else len = 1; if (step[u] + len < step[t[i]]) { if (!len) { q.push_front(t[i]); step[t[i]] = step[u]; } else { q.push_back(t[i]); step[t[i]] = step[u] + 1; } } } } } int binary_search() { l = 0; while (l != r) { int mid = (l + r) >> 1; bool ok = judge(mid); if (ok) { r = mid; } else { l = mid + 1; } } return l; } void addedge(int u, int v, int wei) { t[++cnt] = v; w[cnt] = wei; nxt[cnt] = head[u]; head[u] = cnt; return; } int main() { scanf("%d%d%d", &n, &p, &k); for (int i = 1; i <= p; i++) { int u, v, w; scanf("%d%d%d", &u, &v, &w); r = max(r, w); addedge(u, v, w); addedge(v, u, w); } BFS(); if (!have) { puts("-1"); } else printf("%d\n", binary_search()); return 0; }