A city is served by a number of fire stations. Some residents have complained that the distance from their houses to the nearest station is too far, so a new station is to be built. You are to choose the location of the fire station so as to reduce the distance to the nearest station from the houses of the disgruntled residents.
The city has up to 500 intersections, connected by road segments of various lengths. No more than 20 road segments intersect at a given intersection. The location of houses and firestations alike are considered to be at intersections (the travel distance from the intersection to the actual building can be discounted). Furthermore, we assume that there is at least one house associated with every intersection. There may be more than one firestation per intersection.
Input
The first line of input contains two positive integers: f,the number of existing fire stations (f <= 100) and i, the number of intersections (i <= 500). The intersections are numbered from 1 to i consecutively. f lines follow; each contains the intersection number at which an existing fire station is found. A number of lines follow, each containing three positive integers: the number of an intersection, the number of a different intersection, and the length of the road segment connecting the intersections. All road segments are two-way (at least as far as fire engines are concerned), and there will exist a route between any pair of intersections.
Subsequent test cases are separated with a single blank line.
Output
You are to output a single integer for each test case: the lowest intersection number at which a new fire station should be built so as to minimize the maximum distance from any intersection to the nearest fire station.
Sample Input
1 6
2
1 2 10
2 3 10
3 4 10
4 5 10
5 6 10
6 1 10
Sample Output
5
————————————————————集训16.4的分割线————————————————————
前言:这道题跟POJ-2607除了时限(POJ 5000ms, ZOJ 2000ms)以及测试数据不一样之外,是一样的。POJ上很简单就过了。(如果你注意到了只有1个顶点直接输出1的话)但是ZOJ上怎么也不能过。网上很多所谓题解都是过不了ZOJ的。
隐约感觉POJ上不是多组测试数据,而是单组多case。因为——
整个输入的结束标志(EOF)和各组的输入结束的标志(一个空行)问题。
A number of lines follow, each containing three positive integers
若干行。如果内层使用了EOF,那么就会把下一组测试数据也读进来。POJ上写两个EOF居然也能过。
所以在ZOJ上想过的话,就要用gets或者fgets了,这样读入空行就表示A B L的输入结束了。
我的方法是SPFA,加了一些剪枝,POJ、ZOJ都是0ms过的~
题意:每个顶点都要到达消防站,各顶点有0个或者多个消防站,新建一个消防站,使得各顶点与离其最近的消防站的最大距离最小。
就是说每个顶点都有一个最近消防站,N个顶点中有一个顶点离它最近的消防站最远。要新建这样一个消防站,使得建完之后,图上N个顶点中离最近消防站最远的那个点(设值为B1~Bn),ans为所有B值中最小的。(如果有多个最小,输出i最小的Bi)
思路:题意理解了思路就出来了。先把含有多个消防站的顶点、变成一个消防站,dis初始化0,其余无穷大。然后每个消防站跑一次SPFA(SPFA中不要重新初始化dis,否则每个消防站跑一次就没意义了)。这样最终得到的dis被定义成:所有消防站距离其它顶点的最短路(所有消防站意味着每个顶点都是与其最近的消防站的最短路)。姑且称之为最短最短路吧。枚举维护dis当中的最大值MAXA。
然后复制dis数组保存。接下来枚举某点成为消防站之后得到的新的dis数组,枚举维护最大值MAXB。下次枚举之前记得恢复dis。最后的答案是最小的MAXB。也就是min(MAXA, MAXB1, MAXB2,...,MAXBn)。
P.S. 0ms就不需要用heap优化了吧。。。
代码如下:
/*
ID: j.sure.1
PROG:
LANG: C++
*/
/****************************************/
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <stack>
#include <queue>
#include <vector>
#include <map>
#include <string>
#include <climits>
#include <iostream>
#define INF 0x3f3f3f3f
using namespace std;
/****************************************/
const int N = 505, M = 20*N;
int st[105], n, tot, head[N], q[N], dis[N], newd[N];
bool inq[N], vis[N];
struct Node {
int v, w, next;
}edge[M];
void add(int u, int v, int w)
{
edge[tot].v = v; edge[tot].w = w;
edge[tot].next = head[u]; head[u] = tot++;
}
void spfa(int st)
{
memset(inq, 0, sizeof(inq));
newd[st] = 0;
inq[st] = true;
int fron = 0, rear = 1;
q[fron] = st;
while(fron < rear) {
int u = q[fron%N]; fron++;
inq[u] = false;
for(int i = head[u]; i != -1; i = edge[i].next) {
int v = edge[i].v;
if(newd[v] > newd[u] + edge[i].w) {
newd[v] = newd[u] + edge[i].w;
if(!inq[v]) {
q[rear%N] = v;
rear++;
}
}
}
}
}
int main()
{
#ifdef J_Sure
freopen("000.in", "r", stdin);
// freopen(".out", "w", stdout);
#endif
int fire;
while(~scanf("%d%d", &fire, &n)) {
tot = 0;
memset(head, -1, sizeof(head));
memset(vis, 0, sizeof(vis));
int t, cnt = 0;
for(int i = 1; i <= fire; i++) {
scanf("%d", &t);
if(!vis[t]) {
st[++cnt] = t;
vis[t] = true;
}
}//去除多余的消防站
int u, v, w;
char s[100];
getchar();//吞掉回车,否则WA
while(fgets(s, 100, stdin) && strlen(s)-1) {//听说gets不安全,主要是警告太烦人
s[strlen(s)-1] = '\0';//fgets会保存回车符
sscanf(s, "%d%d%d", &u, &v, &w);
add(u, v, w);
add(v, u, w);
}
if(n == 1) {
puts("1");
continue;
}
for(int i = 1; i <= n; i++) {
if(vis[i]) newd[i] = 0;
else newd[i] = INF;
}
int maxi = -1;
for(int i = 1; i <= cnt; i++) {
spfa(st[i]);
}//未添加新消防站的SPFA
for(int k = 1; k <= n; k++) {
maxi = max(maxi, newd[k]);
}//维护初始最大值
for(int i = 1; i <= n; i++) {
dis[i] = newd[i];
}//保存初始dis数组
int mini = maxi, I;
for(int i = 1; i <= n; i++) if(!vis[i]) {//枚举新的消防站
maxi = -1;
spfa(i);
for(int k = 1; k <= n; k++) {
maxi = max(maxi, newd[k]);
}
if(mini > maxi) {
mini = maxi;
I = i;
}
memcpy(newd, dis, sizeof(dis));//恢复newd数组
}
printf("%d\n", I);
}
return 0;
}