每日一题 疫情防控(最短路Dijkstra)

疫情防控

问题描述: 大福镇上只有一个市场,因此人们只能挤到这个市场买菜。为了限制市场的人口流量,做好疫情防控,政府决定选择性地保留最多 k 条公路,即政府可能需要封掉某些公路。
但是政府需要选择一个合适的封路方案使得影响的村庄数量最少。
大福镇上有个村庄,而连接两村庄的公路是双向的,其中市场位于 1 号村庄。 若这个封路方案对第 i 个村庄有影响是指:在没有封路的情况下村庄 i 到市场的最短路为 dis1,而在封路之后,村庄 i 到市场的最短路为 dis2,而 dis2 > dis1。
你需要做的是找到一个合适的方案,该方案影响的村庄数量最少,或者说,最大化没有影响的村庄的数量。 输出你的方案,若有多种方案可以使得影响程度都是最小的,你只需要输出其中一 种。
输入描述:
第一行为三个整数 n,m,k.
接下来有 m 行,每行三个整数 u,v,w
输出描述:
第一行为一个整数 e ,该方案保留的边数(你不需要最小化 ,你仅需保证 e<=k)
第二行有 e 个整数,每个整数,表示一条保留的边的序号(边的序号是按输入顺序编号的,输出时边的序号可以乱序)

比赛时,市场为1没看到,来回看了几遍都没看懂题目。
保存k条公路,就是保留 1 和 k 个点。
最短路模板,只需将 1 到 k 个点最短路径部分保存编号就行了。
1 到 x 的最短路径肯定建立在 1 到 其他点的最短路径基础上(除非1和x是一条边)。
通过优先队列的特性知道,1 到 x 最短路的前置边也肯定在保存范围了。

#include 
#include 
#include 
using namespace std;
typedef long long ll;
const int N = 3e5 + 5;
struct Edge {
    int to;
    ll dis;
    int id;
    int next;
} e[N << 1];
int head[N];
int ans[N];
int tot;
void add(int u, int v, ll dis, int id) {
    e[++tot] = Edge{v, dis, id, head[u]};
    head[u] = tot;
    e[++tot] = Edge{u, dis, id, head[v]};
    head[v] = tot;
}
ll d[N];
struct Node {
    int u;
    int id;
    ll dis;
    //路径短的优先
    bool operator<(const Node& a) const { return dis > a.dis; }
};
int cnt = 0;
priority_queue<Node> que;
void Dijkstra(int s, int k) {
    Node tmp;
    que.push({s, 0, 0});
    d[s] = 0;
    //因为记录的是点,所以需要记录k+1个点,才有k条边.
    while (!que.empty() && cnt <= k) {
        tmp = que.top();
        que.pop();
        int u = tmp.u;
        if (d[u] == tmp.dis) {
            ans[cnt++] = tmp.id;
            for (int i = head[u]; i; i = e[i].next) {
                int v = e[i].to;
                ll dis = e[i].dis;
                //不能在这里记录编号,可能超过k个
                if (d[v] > d[u] + dis) {
                    d[v] = d[u] + dis;
                    que.push({v, e[i].id, d[v]});
                }
            }
        }
    }
}
int main() {
    int n, m, k;
    memset(d, 0x3f, sizeof(d));
    scanf("%d%d%d", &n, &m, &k);
    int u, v;
    ll dis;
    for (int i = 1; i <= m; i++) {
        scanf("%d%d%lld", &u, &v, &dis);
        add(u, v, dis, i);
    }
    Dijkstra(1, k);
    //边数要减去第一个点
    printf("%d\n", cnt - 1);
    for (int i = 1; i < cnt; i++) {
        printf("%d ", ans[i]);
    }
    return 0;
}

你可能感兴趣的:(每日一题 疫情防控(最短路Dijkstra))