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

全部评论

相关推荐

lxylxy_:其实是美团卷起来了
点赞 评论 收藏
分享
Allen好Iverson:我看牛客都是20-30k的 这个3.9k爆出来有点,哈哈哈哈
点赞 评论 收藏
分享
评论
点赞
收藏
分享
牛客网
牛客企业服务