codeforces D. Edge Deletion+最短路树(优先队列优化)
题目链接:http://codeforces.com/contest/1076/problem/D
题目大意:
在一个n个点m条边的无向图中起点为1,设初始到达第i个点的最短距离为d[i],
现在要求在图上删边,使剩下的边等于k条,并让尽量多的点d[i]与之前相等
最短路:
可以看出来,这就是一棵树,因为是最短路,所以称为最短路树。
保留k条边。在数上bfs或者dfs访问k条边记录下来就可以。
建树只要记录每个节点由哪个节点,哪条边,松弛的就可以了。
dfs保留编号3, 2或者编号5, 3。
bfs保留编号编号3, 1。
思考:第一次遇到这种建树的方法,学习了
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define maxm 600005
#define maxn 300005
#define inf 0x3f3f3f3f3f3f3f3f
struct P
{
int to;
ll cost;
bool operator < (const P & a) const
{
return cost>a.cost;
}
};
struct node
{
int to; //终点
ll val; //权力
int nxt;//同个起点的下一条边(-1)终止
int id; //边的编号
int s;
}edge[maxm];
//head[i]记录了以i为起点的最后一条边的下标,并且可以通过id找到i为起点d的下一条边
//这样就遍历了以i为起点所有边
int head[maxn],tot=0;//head和tot记得重置,head重置为-1
int n,m;//点数,边数,不要再main里面重新定义
bool vis[maxn];//每次在dij里面初始化为0
ll dis[maxn];//根据题意初始化为inf可能int可能longlong
void addedge(int x,int y,ll val,int id)
{
edge[tot].to=y;
edge[tot].val=val;
edge[tot].nxt=head[x];
edge[tot].id=id;
head[x]=tot++;
}
int pre[maxn];//记录每个点的点前驱
int prem[maxn];//每个点的边前驱
//int vv[maxn];
void Dijkstra(int s)
{
memset(vis,0,sizeof(vis));
fill(dis,dis+n+2,inf);
dis[s]=0;
priority_queue<P>q;
q.push(P{s,0});
while(!q.empty())
{
P p1=q.top();q.pop();
int u=p1.to;
if(dis[u] < p1.cost)continue;
//如果目前的值已经比进堆时小了,就没必要再过一遍了,有的题会卡这个时间
vis[u]=1;
for(int i=head[u];i+1;i=edge[i].nxt)
{
int v=edge[i].to;
if(vis[v])continue;//判断节点v是否加入最短路树中
if(dis[v]>dis[u]+edge[i].val)
{
pre[v]=u; //记录每个点的点前驱
prem[v]=edge[i].id;//记录每个点的边前驱
dis[v]=dis[u]+edge[i].val;
q.push(P{v,dis[v]});
}
}
}
}
vector<pair <int, int> > G[maxn];
vector<int> ans;
int ind[maxn];
int cc,k;
void dfs(int st)//对节点深搜
{
if(cc==k) return ;
for(int i=0;i<G[st].size();i++)
{
cc++;
ans.push_back(G[st][i].second);
if(cc==k) return;
dfs(G[st][i].first);
if(cc==k) return ;
}
}
//queue <int> qe;
//void bfs(int st)//对节点广搜
//{
// qe.push(st);
// while(cc!=k)
// {
// int t=qe.front();
// qe.pop();
//
// for(int i=0;i<G[t].size();i++)
// {
// cc++;
// ans.push_back(G[t][i].second);
// qe.push(G[t][i].first);
// if(cc==k)
// return;
// }
// }
//}
int main()
{
int u,v;
ll w;
memset(head,-1,sizeof(head));
scanf("%d%d%d",&n,&m,&k);
for(int i=1;i<=m;i++)
{
scanf("%d%d%lld",&u,&v,&w);
addedge(u,v,w,i);
addedge(v,u,w,i);
}
Dijkstra(1);
if(k>=n-1)
{
printf("%d\n",n-1);
for(int i=2;i<=n;i++)
{
printf("%d ",prem[i]);
}
}
else
{
int re=n-1;
for(int i=2;i<=n;i++)
{
G[pre[i]].push_back(pair <int, int> (i,prem[i]));//重新根据前驱生成一棵树
//节点i到节点G[i].f的边编号为G[i].s
//cout<<pre[i]<<' '<<i<<' '<<prem[i]<<endl;
}
cc=0;
dfs(1);
printf("%d\n",k);
for(int i=0;i<ans.size();i++)
{
printf("%d ",ans[i]);
}
}
return 0;
}