Codeforces Gym101170I:Iron and Coal(建多幅图+多次BFS)***

本文介绍了一种解决占领特定资源点(铁矿和煤矿)的最短路径问题的算法。该算法通过构建正反向图,并进行三次宽度优先搜索(BFS),找到从起点到至少一个铁矿和一个煤矿的最短路径。适用于游戏地图或资源调度场景。

题目链接

题意

有n个点,其中有m个点是铁矿,k个点是煤,从1号点出发,你可以派一些士兵跑向不同的点,问占领至少一个铁矿和一个煤的时候,最少需要占领多少个点。

思路

建两幅图,其中一幅是正向边,一幅是反向边。做三次BFS。
第一遍BFS:从1号点BFS一遍整个正向边的图,记录数组dis[0][i]为每个点距离1号点的距离。O(n)
第二遍BFS:从每个铁矿BFS一遍整个反向边的图,记录数组dis[1][i]为每个点距离每个铁矿的最近距离。O(n)
第三遍BFS:从每个煤BFS一遍整个反向边的图,和第二遍类似。
对于每个点,三个dis数组求和就是从一号点到最近的铁矿和最近的煤的距离和。

#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
const int INF = 0x3f3f3f3f;
vector<int> g[2][N];
int o[N], c[N], b[2] = {1, 1}, dis[3][N];
void bfs(int e, int n, int p[], int id) {
    queue<int> que; while(!que.empty()) que.pop();
    memset(dis[id], INF, sizeof(dis[id]));
    for(int i = 1; i <= n; i++) que.push(p[i]), dis[id][p[i]] = 0;
    while(!que.empty()) {
        int u = que.front(); que.pop();
        for(int i = 0; i < g[e][u].size(); i++) {
            int v = g[e][u][i];
            if(dis[id][v] != INF) continue;
            dis[id][v] = dis[id][u] + 1;
            que.push(v);
        }
    }
}
int main() {
    int n, m, k; scanf("%d%d%d", &n, &m, &k);
    for(int i = 1; i <= m; i++) scanf("%d", &o[i]);
    for(int i = 1; i <= k; i++) scanf("%d", &c[i]);
    for(int i = 0; i < 2; i++) for(int j = 1; j <= n; j++) g[i][j].clear();
    for(int i = 1; i <= n; i++) {
        int a; scanf("%d", &a);
        for(int j = 1; j <= a; j++) {
            int v; scanf("%d", &v);
            g[0][i].push_back(v);
            g[1][v].push_back(i);
        }
    }
    bfs(0, 1, b, 0);
    bfs(1, m, o, 1);
    bfs(1, k, c, 2);
    int ans = INF;
    for(int i = 1; i <= n; i++) {
    //  printf("%d : %d - %d - %d\n", i, dis[0][i], dis[1][i], dis[2][i]);
        if(dis[0][i] == INF || dis[1][i] == INF || dis[2][i] == INF) continue;
        ans = min(ans, dis[0][i] + dis[1][i] + dis[2][i]);
    }
    if(ans == INF) puts("impossible");
    else printf("%d\n", ans);
    return 0;
}

转载于:https://www.cnblogs.com/fightfordream/p/7215320.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值