1. 程式人生 > >codeforces D. Edge Deletion+最短路樹(優先佇列優化)

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