题目描述:
小p和他的朋友约定好去游乐场游玩,但是他们到了游乐场后却互相找不到对方了。
游乐场可以看做是一张n个点,m条道路的图,每条道路有边权wi,表示第一次经过该道路时的花费(第二次及以后经过时花费为0)。
现在,小p要去找他的朋友,但他的朋友行踪很诡异,小p总是要遍历完这n个点才能找到他,同时小p希望总花费最小。
找到朋友的方案可能不唯一(具体看样例解释),小p想知道在这所有的方案中,有多少条边在每个方案中都会被经过。
输入描述:
第一行两个整数n, m. p,分别表示点数,边数,小p的初始位置。 接下来m行,每行两个整数u, v, w表示从u到v有一条无向边,边权为w。
输出描述:
输出一个整数k,表示必须经过的边的数量。
示例1
输入
5 7 1 1 2 3 2 3 7 1 3 5 2 4 2 1 5 3 5 4 3 2 5 3
输出
2
样例解释:
几种可能的方案如下:
可以证明,4 - 2和1 - 3这两条边在所有方案中都被经过。
(以上每种方案的总花费均为13,同时可以证明没有比这更优的策略)
示例2
输入
3 3 1 1 2 1 1 3 1 2 3 2
输出
2
示例3
输入
3 3 1 1 2 2 2 3 2 1 3 2
输出
0
备注:
保证图联通,保证无自环,保证无重边
链接:https://ac.nowcoder.com/acm/contest/272/D
来源:牛客网
简单来说就是寻找有几条边一定在最小生成树上
将路径按照权值排序后,按照权值由小到大的顺序将点增加到最小生成树中,使用并查集维护图的连通性
如果路径权值相等,路径两个端点已经在连通图中,那么不用添加这条边
如果两个端点不在连通图中,那么将边添加到新图中,然后使用Tarjan算法求解出无向图中的桥,即一定存在于最小生成树中的边
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <vector>
using namespace std;
typedef long long ll;
const int maxn = 200100;
int n,m,s;
vector<pair<int,int> > v[maxn]; //v[x] 表示以x开始的节点,pair.first表示边到达的节点,pair.second表示边的编号
struct Edge
{
int u,v,w,id;
bool operator<(const Edge&a)const
{
return w < a.w;
}
}edge[maxn];
int dfn[maxn],low[maxn],k,per[maxn],ans[maxn];
void tarjan(int x,int fa) //Tarjan求解图中的桥
{
dfn[x] = low[x] = ++k;
for(int i = 0,to,id;i < v[x].size();i ++)
{
if((id = v[x][i].second) == fa) continue; //路径返回了回去,不用考虑
to = v[x][i].first;
if(!dfn[to]){
tarjan(to,id);low[x] = min(low[x],low[to]);
if(low[to] > dfn[x]) ans[id] = 1,cout<<edge[id].u<<" "<<edge[id].v<<endl;;
}
else
low[x] = min(dfn[to],low[x]);
}
}
int find(int x)
{
return x == per[x]?per[x]:per[x] = find(per[x]);
}
int main()
{
int x,y,z;
scanf("%d%d%d",&n,&m,&s);
for(int i = 1; i <= n;i ++) per[i] = i;
for(int i = 1;i <= m;i++)
{
scanf("%d%d%d",&x,&y,&z);
edge[i] = (Edge){x,y,z,i};
}
sort(edge+1,edge+1+m);
int nxt;
for(int i = 1;i <= m;i = nxt + 1)
{
nxt = i+1;
for(; edge[i].w == edge[nxt].w; nxt++);
nxt--; //[i-nxt]为路径权值相等的区间范围
for(int j = i;j <= nxt;j ++) //遍历这个区间
{
x = find(edge[j].u),y = find(edge[j].v);
if(x == y) continue; //如果已经连通,则不需要考虑这条边
v[x].push_back(make_pair(y,j)); //否则将这条边添加到图中
v[y].push_back(make_pair(x,j));
}
for(int j = i;j <= nxt;j ++)
{
x = find(edge[j].u),y = find(edge[j].v);
if(x == y || dfn[x]) continue;
tarjan(x,-1);
}
for(int j = i;j <= nxt;j ++)
{
x = find(edge[j].u),y = find(edge[j].v);
if(x == y) continue;
v[x].clear();v[y].clear();
dfn[x] = 0;dfn[y] = 0;
per[x] = y;
}
}
int tot = 0;
for(int i = 1;i <= m;i ++) tot += ans[i];
printf("%d\n",tot);
return 0;
}