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;
}

浙公网安备 33010602011771号