gym 101170 NWERC 2016 I Iron and Coal

来源:互联网 发布:开源房产cms 编辑:程序博客网 时间:2024/05/17 23:56

Problem

Northwestern European Regional Contest 2016
vjudge.net/problem/Gym-101170I

Meaning

一幅 n 个点的有向图,m 个点有铁矿、k 个点有煤,你一开始在 1 号点,至少要占领一个铁和一个煤,问至少要占领多少个点(除了开始的 1 号点外)

Analysis

应该要尝试找到任意一个铁,再从这个铁出发找最近的煤(或者反过来),但这样太暴离力了。其实就是 点到起点最短距离、点到最近铁、点到最近煤 三个距离的和的最小值。
理解题意时有过分歧:是第一人称视角还是第三人称视角(我们当时叫个人视角和上帝视角)。如果是个人视角,那就只能从当前所在点为起点往外走;如果是上帝视角,则可以从任意一个已占领的点为起点往外走。如样例:

3 1 1232 2 300

个人视角是impossible,上帝视角则是2
从 dalao 的代码来看,是上帝视角
dalao 的做法是建两幅图,一幅存正向边,用来算从起点到其它点的最短距离(单源点bfs);另一幅存反向边,用于求点到最近铁/煤的距离(全部铁/煤点同时当起点开始bfs),然后遍历所有点,把 3 个距离加和求最小值。

Code

#include <cstdio>#include <cstring>#include <algorithm>#include <queue>#include <vector>using namespace std;const int N = 200000, M = N, K = N, BIG = 123456789;int s[] = {1}, o[M], c[K]; // source pointint dis[3][N+1];vector<int> edge[N+1], rev[N+1];queue<int> que;void bfs(int *src, int n, int num, int id, vector<int> *e){    for(int i = 0; i <= num; ++i)        dis[id][i] = BIG;    for(int i = 0; i < n; ++i)    {        dis[id][src[i]] = 0;        que.push(src[i]);    }    for(int t; !que.empty(); que.pop())    {        t = que.front();        for(int i = 0; i < e[t].size(); ++i)            if(dis[id][e[t][i]] == BIG)            {                dis[id][e[t][i]] = dis[id][t] + 1;                que.push(e[t][i]);            }    }}int main(){    int n, m, k;    scanf("%d%d%d", &n, &m, &k);    for(int i = 0; i < m; ++i)        scanf("%d", o+i);    for(int j = 0; j < k; ++j)        scanf("%d", c+j);    for(int i = 1, a; i <= n; ++i)    {        scanf("%d", &a);        for(int j = 0, b; j < a; ++j)        {            scanf("%d", &b);            edge[i].push_back(b);            rev[b].push_back(i);        }    }    bfs(s, 1, n, 0, edge);    bfs(o, m, n, 1, rev);    bfs(c, k, n, 2, rev);    int ans = BIG;    for(int i = 1; i <= n; ++i)        ans = min(ans, dis[0][i] + dis[1][i] + dis[2][i]);    if(ans == BIG)        puts("impossible");    else        printf("%d\n", ans);    return 0;}