这题不会,记住写法就行了,好像都是这么写的?
第一次从最小边开始往上添,第二次从第二小边开始。。。每次添边直到那俩点连通,每次记录该次的极差并更新最优值。
可得到的结论是,第i+1
次添的最后一条边肯定不会比第i
次添的最后一条边权值还小。
(2019年4月12日更新)这道题和HDU 2363太像了。所以今天我用另一种方法又做了一遍。(第二份代码)
这道题其实就是个无向图,然后给定起点终点,找一条路,这条路上的边权的极值的差值最小。输出这个最小的极值差值。
和HDU 2363的区别在于,这道题是边权,那道题是点权;而且这道题不需要输出在此基础上最短路的值。
#include <iostream>
#include <algorithm>
#include <vector>
#include <cstring>
using namespace std;
const int INF = 1e9;
const int MAXN = 205;
int N, M, Q;
int opt;
struct Edge
{
int n1, n2, sp;
bool operator<(const Edge& e) const
{
return sp < e.sp;
}
};
vector<Edge> v;
int pre[MAXN];
int f(int x)
{
int f0 = x, f1 = x;
for (; pre[f0] > 0;)
f0 = pre[f0];
for (; pre[f1] > 0;)
{
int t = f1;
f1 = pre[f1];
pre[t] = f0;
}
return f0;
}
void u(int n1, int n2)
{
int f1 = f(n1);
int f2 = f(n2);
if (f1 != f2)
{
if (pre[f1] <= pre[f2])
{
pre[f1] += pre[f2];
pre[f2] = f1;
}
else
{
pre[f2] += pre[f1];
pre[f1] = f2;
}
}
}
int main()
{
int a, b, c;
bool flag;
for (; ~scanf("%d%d", &N, &M);) // 别忘加 ~
{
v.clear();
for (int i = 0; i < M; i++)
{
scanf("%d%d%d", &a, &b, &c);
v.push_back({ a,b,c });
}
sort(v.begin(), v.end());
scanf("%d", &Q);
for (int i = 0; i < Q; i++)
{
flag = false;
opt = INF;
scanf("%d%d", &a, &b);
for (int j = 0; j < v.size(); j++)
{
memset(pre, -1, sizeof pre);
for (int k = j; k < v.size(); k++)
{
u(v[k].n1, v[k].n2);
if (f(a) == f(b))
{
opt = min(opt, v[k].sp - v[j].sp);
break;
}
if (k == v.size() - 1) flag = true;
}
if (flag) break;
}
if (opt == INF) printf("-1\n");
else printf("%d\n", opt);
}
}
return 0;
}
下面是第二份代码。需要注意几个点。
- 由于可能本来就不连通,那么每次查询给定两点后就
spfa
一遍,先排除这种情况。 - 这两种做法其实都要枚举出来上下限。下面的做法是通过查找是否有最短路来判断连不连通,因为这道题不需要输出最短路的值,所以在给定差值以后,只要有一个上下限能连通(最短路已经有保底了),那么就可以去二分下一个更小的差值了,不用再找这个差值情况下的最短路了。
// 1598的另一种做法,用二分限制最短路(HDU 2363)求解。
#include <iostream>
#include <algorithm>
#include <vector>
#include <cstring>
#include <utility>
#include <queue>
using namespace std;
const int INF = 1e9;
const int MAX = 205;
int N, M, Q;
vector<pair<int, int>> v[MAX];
int max_speed, min_speed;
int d[MAX];
bool inq[MAX];
vector<int> ve;
void init()
{
for (int i = 1; i <= N; i++)
v[i].clear();
max_speed = -1;
min_speed = INF;
ve.clear();
}
void spfa(int s, int low_speed, int high_speed)
{
queue<int> q;
fill(d + 1, d + N + 1, INF);
memset(inq, 0, sizeof inq);
q.push(s);
d[s] = 0;
inq[s] = true;
for (; !q.empty();)
{
int x = q.front();
q.pop();
inq[x] = false;
for (int i = 0; i < v[x].size(); i++)
{
int n = v[x][i].first;
int w = v[x][i].second;
if (w > high_speed || w < low_speed) continue;
if (d[x] + w < d[n])
{
d[n] = d[x] + w;
if (!inq[n])
{
q.push(n);
inq[n] = true;
}
}
}
}
}
int main()
{
int a, b, c;
for (; ~scanf("%d%d", &N, &M);)
{
init();
for (int i = 0; i < M; i++)
{
scanf("%d%d%d", &a, &b, &c);
v[a].push_back(make_pair(b, c));
v[b].push_back(make_pair(a, c));
max_speed = max(max_speed, c);
min_speed = min(min_speed, c);
ve.push_back(c);
}
scanf("%d", &Q);
for (; Q--;)
{
int l = 0, r = max_speed - min_speed;
int mid, ans;
scanf("%d%d", &a, &b);
spfa(a, min_speed, max_speed);
if (d[b] == INF)
{
printf("-1\n");
continue;
}
for (; l < r;)
{
mid = (l + r) >> 1;
ans = INF;
for (int i = 0; i < ve.size(); i++)
{
spfa(a, ve[i], ve[i] + mid);
ans = min(ans, d[b]);
//cout << "ans=" << ans << endl;
if (ans != INF) break; // 我们只是借用这个最短路值来判断可行性,又不用输出它,所以只要证明能连通就行
} // 这句没有就TLE了
if (ans != INF) r = mid;
else l = mid + 1;
}
printf("%d\n", l);
}
}
return 0;
}