cf Educational Codeforces Round 54 D. Edge Deletion

原题:
D. Edge Deletion
time limit per test2.5 seconds
memory limit per test256 megabytes
inputstandard input
outputstandard output
You are given an undirected connected weighted graph consisting of n vertices and m edges. Let’s denote the length of the shortest path from vertex 1 to vertex i as di.

You have to erase some edges of the graph so that at most k edges remain. Let’s call a vertex i good if there still exists a path from 1 to i with length di after erasing the edges.

Your goal is to erase the edges in such a way that the number of good vertices is maximized.

Input
The first line contains three integers n, m and k ( 2 ≤ n ≤ 3 × 1 0 5 2≤n≤3×10^5 2n3×105, 1 ≤ m ≤ 3 × 1 0 5 1≤m≤3×10^5 1m3×105, n−1≤m, 0≤k≤m) — the number of vertices and edges in the graph, and the maximum number of edges that can be retained in the graph, respectively.

Then m lines follow, each containing three integers x, y, w (1≤x,y≤n, x≠y, 1≤w≤10^9), denoting an edge connecting vertices x and y and having weight w.

The given graph is connected (any vertex can be reached from any other vertex) and simple (there are no self-loops, and for each unordered pair of vertices there exists at most one edge connecting these vertices).

Output
In the first line print e — the number of edges that should remain in the graph (0≤e≤k).

In the second line print e distinct integers from 1 to m — the indices of edges that should remain in the graph. Edges are numbered in the same order they are given in the input. The number of good vertices should be as large as possible.

Examples
input
3 3 2
1 2 1
3 2 1
1 3 3
output
2
1 2
input
4 5 2
4 1 8
2 4 1
2 1 3
3 4 9
3 1 5
output
2
3 2

中文:
给你一个无向图,有n个节点m条边,先找到从顶点1到所有顶点的最短路径,然后让你去掉一些边,使得最后剩下的边数小于等于k条。最后,如果去掉一些边后,如果某个顶点的最短路径大小仍然没有改变,那么这个顶点就是一个"good vertex",现在让你在保留最多k条边的情况下,尽量等到最多的"good vertex"

结果输出两行,第一行整数表示剩下的边数
第二行输出,剩下的边对应的编号,编号就是在输入的m个边的顺序。

代码:

#include<bits/stdc++.h>
using namespace std;

typedef long long ll;
typedef pair<ll,ll> pii;


int N,M,K;

map<pii,int> mpi;//边->编号
set<pii> mark;//起点到所有顶点最短路径的边
const int maxn=300001;
const ll INF=1e17;
int out[maxn];//每个顶点的出度

struct edge
{
    ll to,cost;
};
vector<edge> G[maxn];
ll d[maxn];
bool used[maxn];
ll prior[maxn];
vector<int> ans;

void dijkstra(int s)
{
    priority_queue<pii,vector<pii>,greater<pii> > que;
    memset(used,false,sizeof(used));
    fill(d+1,d+N+1,INF);

    d[s]=0;
    que.push(pii(0,s));
    while(!que.empty())
    {
        pii p=que.top();
        que.pop();
        int v=p.second;
        if(d[v]<p.first||used[v])
            continue;
        used[v]=true;
        for(int i=0;i<G[v].size();i++)
        {
            edge e=G[v][i];
            if(d[e.to]>d[v]+e.cost)
            {
                d[e.to]=d[v]+e.cost;
                prior[e.to]=v;//记录在最短路径当中,每个顶点在计算最短路后接到的顶点
                que.push(pii(d[e.to],e.to));
            }
        }
    }
}
int main()
{
    ios::sync_with_stdio(false);
	while(cin>>N>>M>>K)
    {
        mpi.clear();
        mark.clear();
        int ind=1;
        ll f,t,w;
        ans.clear();
        memset(out,0,sizeof(out));
        memset(prior,-1,sizeof(prior));
        for(int i=1;i<=M;i++)
        {
            cin>>f>>t>>w;
            G[f].push_back(edge{t,w});
            G[t].push_back(edge{f,w});
            pii p1(f,t),p2(t,f);
            mpi[p1]=ind;
            mpi[p2]=ind;//边对应的编号
            ind++;
        }
        dijkstra(1);
        for(int i=1;i<=N;i++)
        {
            if(prior[i]!=-1)
                out[prior[i]]++;//出度为0的优先
        }
        if(K>=N-1)
        {
            cout<<N-1<<endl;
            for(int i=2;i<=N;i++)//要按照序号的顺序输出
            {
                pii p(i,prior[i]);
                ans.push_back(mpi[p]);
            }
            sort(ans.begin(),ans.end());
            for(int i=0;i<ans.size();i++)
            {
                if(i==ans.size()-1)
                    cout<<ans[i]<<endl;
                else
                    cout<<ans[i]<<" ";
            }
            continue;
        }
		//按照出度为0的优先计算拓扑排序
        for(int i=2;i<=N;i++)
            mark.insert(make_pair(prior[i],i));//最短路径上的所有边
        queue<int> Q;
        int res=N-1-K;//需要减掉的边数
        int ver=N-1;//顶点数
        for(int i=1;i<=N;i++)
        {
            if(out[i]==0)
                Q.push(i);
        }
        while(!Q.empty())
        {
            int u=Q.front();
            Q.pop();
            res--;
            ver--;
            pii p=make_pair(prior[u],u);
            mark.erase(p);
            out[prior[u]]--;
            if(out[prior[u]]==0)
                Q.push(prior[u]);
            if(res==0)
                break;
        }
        cout<<ver<<endl;
        int s=mark.size();
        for(auto x:mark)
        {
            if(s>0)
                cout<<mpi[x]<<" ";
            else
                cout<<mpi[x];
        }
        cout<<endl;

    }
	return 0;
}

解答:

题目中都提到最短路径了,首先肯定会想到最短路算法。

在一个图中,如果想要去掉一些边,使得最短路的值不发生变化,那么首先可以去掉的边一定是不在最短路径中的那些边。

那么,第一步要做的就是先将所有最短路径中的边找出来,其余的边丢掉,检查结果是否满足剩余的边小于等于k条这个要求。可以在dijkstra算法的松弛过程当中使用一个prior数组记录当前节点v所连接到路径最短的那个节点,这样就能找到所有最短路径中的边,而且还会得到的一个以起点出发的有向图。

这里注意,无向图中最短路径的边数一定是顶点数N减去1。所以检查N-1与k的大小关系可以省略一部分计算。

如果仅仅减去非最短路径中的边仍然不满足题目的k限制,那么就要继续去掉一些边,这里按照出度从小到大来不断去掉最短路径中的边,这样每次就会仅仅减少一个最短路径。如下图为得到的最短路径
在这里插入图片描述

如果先去掉<1,2>这这条边,那么会直接减少5条最短路径,如果去掉<6,7>则会减少一条最短路径。

最终输出答案即可

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值