题目链接:http://codeforces.com/problemset/problem/1076/D
题意:一个 n 个顶点 m 条边的无向图,定义顶点 v 到 顶点 1 的最短距离 di,现在最多留下 k 条边,如果一个顶点,在新遗留的图中到顶点 1 的最短距离还是 di,则称这个点为好顶点,问在保证好顶点最多的情况下,应该讲哪些边留下来。 输出留下的边数以及输入时这些边的编号。
思路:最短路树:在跑最短路的过程中,出掉多余的边,留下来的是一颗 n - 1 条边的树,称之为最短路树。而在最短路树上的节点一定是好顶点,所以遗留下来的边数为 e = min(k,n-1)。dijstra 算法在松弛的过程中,用数组 f[v] = u,将这条边以及它的序号保留下来。然后再将这些边重新建图-成一颗树,广度遍历取 e 条边。
AC代码:
#include<bits/stdc++.h>
using namespace std;
#define per(i,a,n) for (int i=a;i<n;i++)
#define rep(i,a,n) for (int i=n-1;i>=a;i--)
#define runfile freopen("E:/AllCode/codeblocks project/Root/data.txt", "r", stdin)
#define stopfile fclose(stdin)
#define tt long long
#define MAX 100050
typedef long long ll;
const int maxn = 300005;
const int maxm = 300005;
const long long INF = LONG_LONG_MAX;
const int MOD = 998244353;
struct edge
{
int u,v,order,next;
long long w;
edge(){}
edge(int _u, int _v, long long _w, int _order, int _next) : u(_u), v(_v), w(_w), order(_order), next(_next){}
}g[2*maxm],tree[2*maxm];
struct node
{
int v;
long long w;
bool operator < (const node &a) const
{
return w > a.w;
}
}dis[maxn];
struct father
{
int u;
int order;
}f[maxn];
int n,m,k,s,e,cnt,cnt1,first[maxn],first1[maxn],vis[maxn],ans[maxm];
void init()
{
memset(vis, 0, sizeof(vis));//mark the edges
for(int i = 1; i <= n; i++)
{
dis[i].v = i;
dis[i].w = INF;
first[i] = -1;
first1[i] = -1;
f[i].u = i;
}
cnt = 1;
cnt1 = 1;
s = 1;
e = min(n-1, k);
}
void add_edge(int u, int v, long long w)
{
g[cnt] = edge(u, v, w, cnt, first[u]);
first[u] = cnt;
//undirected graph
int temp = cnt + m;
g[temp] = edge(v, u, w, temp, first[v]);
first[v] = temp;
cnt++;
}
void add_edge1(int u, int v, long long w, int o)
{
tree[cnt1] = edge(u, v, w, o, first1[u]);
first1[u] = cnt1;
cnt1++;
}
void dijstra_tree(int s)
{
priority_queue<node> pq;
dis[s].v = s;
dis[s].w = 0;
pq.push(dis[s]);
while(!pq.empty())
{
node now = pq.top();
pq.pop();
int u = now.v;
if(vis[u]) continue;
vis[u] = true;
for(int i = first[u]; i != -1; i = g[i].next)
{
int v = g[i].v;
long long w = g[i].w;
int o = g[i].order > m ? g[i].order - m : g[i].order;
if(!vis[v] && (w + dis[u].w < dis[v].w))
{
dis[v].w = w + dis[u].w;
f[v].u = u;//save father node
f[v].order = o;//save start order
// add_edge1(u, v, w, o);
pq.push(dis[v]);
}
}
}
//create tree
for(int i = 1; i <= n; i++)
{
if(f[i].u != i)
{
add_edge1(f[i].u, i, 0, f[i].order);
}
}
}
int delete_edge()
{
queue<int> q;
q.push(1);
int j = 0;
while(!q.empty())
{
int u = q.front();
q.pop();
for(int i = first1[u]; i != -1; i = tree[i].next)
{
if(j >= e) break;
ans[j] = tree[i].order;
j++;
q.push(tree[i].v);
}
}
return j;
}
int main()
{
//runfile;
ios::sync_with_stdio(false);
int from, to, weight;
while(cin>>n>>m>>k)
{
init();
for(int i = 0; i < m; i++)
{
cin>>from>>to>>weight;
add_edge(from, to, weight);
}
dijstra_tree(s);
int j = delete_edge();
printf("%d\n",e);
if(e != 0)
{
for(int i = 0; i < j-1; i++)
printf("%d ",ans[i]);
printf("%d\n",ans[j-1]);
}
}
//stopfile;
return 0;
}