题目链接:http://codeforces.com/contest/1076/problem/D
题意是输入n,m,k,表示有n个点,m条边,需要保留k条边,现在要保证1-n的最短距离不变,然后进行删边,要至少保留k条边且使包含的节点数最多。
思路就是先用dij跑一遍1-n的最短路(spfa会被卡),然后再用dij的思想去跑一遍bfs,把最短路的边都存下来,其实就是一个记录路径的过程,然后输出前k个。这道题的坑点很多,因为权值是1e9的所以要开ll,然后对于inf也要初始化为1e18,还有我用的链式前向星,存边的话数组要开二倍,不然会RE on 7(别问我怎么知道的),其他的应该就没什么了,主要就是细节需要细心点...
AC代码:
#include <bits/stdc++.h>
#define maxn 300005
#define inf 1e18
#define ll long long
using namespace std;
struct Node{
int to,next,id;
ll w;
bool operator < (const Node &a) const {
return a.w < w;
}
}Edge[maxn << 1],Now,Next;
int head[maxn],num;
ll dist[maxn];
bool vis[maxn];
vector<int> v;
int n,m,k;
void init(){
memset(head,-1,sizeof(head));
num = 0;
}
void add(int u, int v,ll w,int id){
Edge[num].to = v;
Edge[num].w = w;
Edge[num].id = id;
Edge[num].next = head[u];
head[u] = num ++;
}
void dijkstra(){
memset(vis,false,sizeof(vis));
for(int i=1;i<=n;i++) dist[i] = inf;
priority_queue<Node> q;
Now.to = 1;
dist[1] = 0;
q.push(Now);
while(!q.empty()){
Now = q.top();
q.pop();
int ans = Now.to;
if(vis[ans]) continue;
vis[ans] = true;
for(int i=head[ans];i!=-1;i=Edge[i].next){
int to = Edge[i].to;
if(!vis[to] && dist[to] > dist[ans] + Edge[i].w){
dist[to] = dist[ans] + Edge[i].w;
Next.to = to;
Next.w = dist[to];
q.push(Next);
}
}
}
}
void solve(){
memset(vis,false,sizeof(vis));
vis[1] = true;
queue<int> q;
q.push(1);
while(!q.empty()){
int ans = q.front();
q.pop();
for(int i=head[ans];i!=-1;i=Edge[i].next){
int to = Edge[i].to;
if(!vis[to] && dist[to] == dist[ans] + Edge[i].w){
vis[to] = true;
dist[to] = dist[ans] + Edge[i].w;
v.push_back(Edge[i].id);
q.push(to);
}
}
}
}
int main()
{
init();
scanf("%d%d%d",&n,&m,&k);
for(int i=1;i<=m;i++){
int u,v;
ll w;
scanf("%d%d%lld",&u,&v,&w);
add(u, v, w, i);
add(v, u, w, i);
}
dijkstra();
solve();
int cnt = v.size();
if(cnt > k) cnt = k;
printf("%d\n",cnt);
for(int i=0;i<cnt;i++){
printf("%d%c",v[i],i==cnt-1?'\n':' ');
}
return 0;
}