cf1076 D. Edge Deletion(最短路)

题意:

删去一棵树中的一些边,只保留最多 k 条边,使到1号节点的距离仍为原来的最短距离的点尽量多。求一种方案,即输出要保留哪些边。

思路:

Dijkstra。当某个点被标记为 v[i]=1 时,这个点的最短距离就被最终确定。注意除此之外其他的操作好像都是不确定的。应该把 “已确定最小距离的点” 作为切入点。

k 条边最多能确定 k+1 个点的最小距离,其中一个是根节点。所以被确定的点数不能超过 k+1 。当某个点 \(y\) 的最短距离被确定时,把最后更新 \(y\) 的边 \(last[y]\) 加入答案。

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const int N = 3e5 + 5, M = N * 2;
int h[N], e[M], ne[M], w[M], idx, no[M];
void add(int a, int b, int c, int i)
{ //要维护每条边的序号
    e[++idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx, no[idx] = i;
}

int n, m, k, cnt; //已确定最短距离的点数
int last[N]; //每个点最后被哪条边更新
vector<int> ans;
ll d[N]; bool v[N];
void dij()
{
    memset(d, 0x3f, sizeof d); d[1] = 0;
    priority_queue<pair<ll, int> > q; q.push({0, 1});

    while(q.size())
    {
        int x = q.top().second; q.pop();
        if(v[x]) continue; v[x] = 1;

        if(x > 1) ans.push_back(last[x]); //根不算
        if(++cnt == k + 1) return; //又一个点被确定

        for(int i = h[x]; i; i = ne[i])
        {
            int y = e[i], z = w[i];
            if(d[y] > d[x] + z)
            {
                d[y] = d[x] + z;
                q.push({-d[y], y});

                last[y] = no[i]; //更新last
            }
        }
    }
}

signed main()
{
    scanf("%d%d%d", &n, &m, &k);
    for(int i = 1; i <= m; i++)
    {
        int a, b, c; scanf("%d%d%d", &a, &b, &c);
        add(a, b, c, i), add(b, a, c, i);
    }

    dij();

    printf("%d\n", ans.size());
    for(int i : ans) printf("%d ", i);

    return 0;
}

posted @ 2021-12-20 22:42  Bellala  阅读(78)  评论(0)    收藏  举报