gym 101170 NWERC 2016 I Iron and Coal

15 篇文章 0 订阅

Problem

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

Meaning

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

Analysis

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

3 1 1
2
3
2 2 3
0
0

个人视角是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 point
int 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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值