分析:这道题实在是不好想,一个可以骗分的想法是假定要求的那个点在中心点上,可以骗得不少分.但是在边上的点要怎么确定呢?理论复杂度O(﹢无穷).答案一定是和端点有关的,涉及到最大值最小,考虑二分最大值,关键就是怎么check.如果有一条边x,还有一个点y,把y到x上的点的距离>mid的点给标记上,这样就会形成许多个区间,判断一下x是否被这些区间给完全覆盖住了,如果没有被完全覆盖住,证明这个最大值可以更小.思路非常奇妙,具体实现细节可以参看代码:
#include <cstdio> #include <cstring> #include <iostream> #include <algorithm>using namespace std;const int maxn = 210, maxm = 40010;int n, m, a[maxn][maxn], head[maxn], to[maxm],ans,len,top,nextt[maxm],w[maxm], tot = 1; struct node {int x, y, z; }e[20000];struct node2 {int first, second; }e2[20000];void add(int x, int y, int z) {w[tot] = z;to[tot] = y;nextt[tot] = head[x];head[x] = tot++; }void floyd() {for (int k = 1; k <= n; k++)for (int i = 1; i <= n; i++)for (int j = 1; j <= n; j++)if (a[i][j] > a[i][k] + a[k][j])a[i][j] = a[i][k] + a[k][j]; }void add2(int x, int y) {e2[++top].first = x;e2[top].second = y; }bool cmp(node2 a, node2 b) {if (a.first == b.first)return a.second < b.second;return a.first < b.first; }bool can() {int x = 0;sort(e2 + 1, e2 + 1 + top,cmp); for (int i = 1; i <= top; i++){if (x < e2[i].first)return false;if (x > e2[i].second)continue;x = e2[i].second + 1;}if (x > len)return true;return false; }bool check(int p) {bool flag = false;for (int i = 1; i <= m; i++){top = 0;len = e[i].z;for (int j = 1; j <= n; j++){int x = p - a[e[i].x][j];int y = p - a[e[i].y][j];if (x < 0 && y < 0){add2(0, e[i].z);break;}if (x >= e[i].z || y >= e[i].z)continue;if (x + y >= e[i].z)continue;add2(max(0, x + 1), min(e[i].z, e[i].z - y - 1));}if (!can())flag = 1;if (flag)break;}if (flag)return true;return false; }int main() {memset(a, 127 / 3, sizeof(a)); scanf("%d%d", &n, &m);for (int i = 1; i <= m; i++){int u, v, z;scanf("%d%d%d", &u, &v, &z);z *= 2;add(u, v, z);add(v, u, z);e[i].x = u;e[i].y = v;e[i].z = z;a[u][v] = a[v][u] = min(a[u][v], z);}for (int i = 1; i <= n; i++)a[i][i] = 0;floyd();int l = 0, r = 10000010;while (l <= r){int mid = (l + r) >> 1;if (check(mid)){ans = mid;r = mid - 1;}elsel = mid + 1;}double temp = ans / 2.0;printf("%.2lf\n", temp); return 0; }