二分枚举最短路。和HDU 1839几乎完全一样。
都是先最小找最大(边权cap
),然后再求最短路(边权len
),无向图。这道题说的什么高度和运量是一个意思。限制高度就是限制运量,最大化运量就是最大化高度。
不同就是这道题不限制最短路上限,而是对最大运量加了一个上限L
。这道题的二分数组是[L...0]
,直接存的就是最大值到最小值的连续区间。与另一种做法:存每条边的容量再从大到小排序再去除大于L
的值是等价的。
另外,这道题需要输出最短路的值,所以二分运行到最后l=r
之后还要再来一次spfa
。
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <vector>
#include <cstring>
#include <string>
#include <queue>
using namespace std;
const int INF = 1e9;
const int MAXN = 1001;
int N, M, S, T, L; // L:全局上限
struct Edge
{
int n, cap, len;
};
vector<Edge> ve;
vector<int> v[MAXN];
int limit;
int d[MAXN];
bool vis[MAXN];
void init()
{
ve.clear();
for (int i = 1; i <= N; i++)
v[i].clear();
}
void spfa(int s)
{
queue<int> q;
memset(vis, 0, sizeof vis);
fill(d + 1, d + N + 1, INF);
q.push(s);
vis[s] = true;
d[s] = 0;
for (; !q.empty();)
{
int t = q.front();
q.pop();
vis[t] = false;
for (int i = 0; i < v[t].size(); i++)
{
int n = ve[v[t][i]].n;
int cap = ve[v[t][i]].cap;
int len = ve[v[t][i]].len;
if (cap >= limit) //
{
if (d[t] + len < d[n])
{
d[n] = d[t] + len;
if (!vis[n])
{
q.push(n);
vis[n] = true;
}
}
}
}
}
}
bool cmp(const int& a, const int& b)
{
return a > b;
}
int main()
{
int a, b, c, dd;
for (int cnt = 1; ~scanf("%d%d", &N, &M); cnt++)
{
if (!N && !M) break;
init();
for (int i = 0; i < M; i++)
{
scanf("%d%d%d%d", &a, &b, &c, &dd);
if (c == -1) c = INF;
ve.push_back(Edge{ b,c,dd });
ve.push_back(Edge{ a,c,dd });
v[a].push_back(i << 1);
v[b].push_back(i << 1 | 1);
}
scanf("%d%d%d", &S, &T, &L);
vector<int> vc;
for (int i = L; i >= 0; i--)
vc.push_back(i);
int l = 0, r = L, mid;
for (; l <= r;) //
{
mid = (l + r) >> 1;
limit = vc[mid];
spfa(S);
if (l == r) break; //
if (d[T] != INF) r = mid;
else l = mid + 1;
}
//limit = vc[l]; // 果然,能找对位置,但d[T]的最后正确输出不能保证,需要再来一次
//spfa(S); // 不加这两句就WA
if (cnt > 1) printf("\n"); // PE
printf("Case %d:\n", cnt);
if (d[T] == INF) printf("cannot reach destination\n");
else
{
printf("maximum height = %d\n", vc[l]);
printf("length of shortest route = %d\n", d[T]);
}
}
return 0;
}