HDU 2377 最短路

来源:互联网 发布:搜游网络 融资 编辑:程序博客网 时间:2024/06/06 12:46
/*
    最短路, 终点集合到s的最远距离最短,求s.    即已知终点集{d}求一s使得Min{ max{ dis(s, di) } }
    好题
    
  
  
    思路:  多次单源最短路,选出最大值
            在对每个x进行分层搜索的过程中, 用max_d[y]记录每个地区x到达地区y的最短距离中的最大值. 最后求得的Star Value就是max_d[]中的最小值.
            由于题目的特殊性`边权都为1`,所以可以借助这一性质变换一下SPFA使其更快。
            说个题外话,在临高时看到有个学弟拓扑排序用到“分层思想”,一直觉得很妙。就是拓扑后我们可以得到floor[i],如果floor[i] > floor[j],即说明j是i的前驱节点(层数越小越接近root); 而floor[i] == floor[j]的话则i,j的相对顺序无所谓,因为他们都在“同一层”。
            这里因为边权都为1,所以SPFA可以用到上述的分层思想,层数越高,离source越远。代码里面floors就表示层数,Q是滚动队列,就是一层一层地relax后继节点。
            注意!!千万不要以为max_d[]是最短路算法里面的dis[],这里的max_d[i]是到点i到终点集合{di}的最大值!而常规最短路算法里的dis[]已经被省略为“层数”了,不需要记录,所以没开数组。
            最重要的是学到一个tip!!以前我做多次最短路的时候总要每次都初始化visit[] -> false,但其实不用的,我们只要用一个变量when表示“这是第几次做SPFA(或其他)“,然后每次入队前都看”是否当前visit[v] == when就可以直到改点是否已经入过队......
*/

#include <stdio.h>#include <string.h>#include <vector>#include <deque>using namespace std;#define     debug   printf("!\n")#define     INF     999999999#define     MAXN    10000int n;int max_d[MAXN];int visit[MAXN];vector<int> v[MAXN];void SPFA(int s, int when);void init();int main(){    int cases, query, id, m, y, x;    scanf("%d", &cases);    while(cases--) {        scanf("%d%d", &n, &query);        init();        for(int i = 0; i < n; i++) {            scanf("%d%d", &id, &m);            while(m--) {                scanf("%d", &y);                v[id].push_back(y);            }        }        int when = 0;        while(query--) {            scanf("%d", &m);            while(m--) {                scanf("%d", &x);                SPFA(x, ++when);            }        }                int ans = INF, ans_id = -1;        for(int i = 1; i < MAXN; i++) if(!v[i].empty() && max_d[i] < ans) ans = max_d[i], ans_id = i;        printf("%d %d\n", ans, ans_id);    }    return 0;}void init(){    for(int i = 0; i < MAXN; i++) v[i].clear();    memset(max_d, 0, sizeof(max_d));    memset(visit, 0, sizeof(visit));}void SPFA(int s, int when){    deque<int> Q[2];    int cur = 0;    Q[cur].push_back(s);    max_d[s] = max(max_d[s], 1);    visit[s] = when;    int floors = 1;                 do {        floors++;        while(!Q[cur].empty()) {            int at = Q[cur].front();            Q[cur].pop_front();            for(int Size = v[at].size(), i = 0; i < Size; i++) {                int to = v[at][i];                if(visit[to] != when) {     //是否已入队                    //max_d[to] = max(max_d[to], max_d[at]+1);      这句是不对的,因为这个分层跟拓扑排序的分层是不一样的,拓扑排序是要在入度为0时才能加进队Q,所以可以这样写,但是这里只要第一次遇见点to就必须得入队,因为要的是最短路径                    max_d[to] = max(max_d[to], floors); //不把这句放在if外面,因为这里的max_d[to]是距离s的最短路径,最短路径也就是最小层数,最小层数在to第一次入队的时候已经得到了                    visit[to] = when;                    Q[1-cur].push_back(to);                }            }        }        cur = 1 - cur;    } while(!Q[cur].empty());}


原创粉丝点击