题目:
曹操在长江上建立了一些点,点之间有一些边连着。如果这些点构成的无向图变成了连通图,那么曹操就无敌了。刘备为了防止曹操变得无敌,就打算去摧毁连接曹操的点的桥。但是诸葛亮把所有炸弹都带走了,只留下一枚给刘备。所以刘备只能炸一条桥。
题目给出n,m。表示有n个点,m条桥。
接下来的m行每行给出a,b,c,表示a点和b点之间有一条桥,而且曹操派了c个人去守卫这条桥。
现在问刘备最少派多少人去炸桥。
如果无法使曹操的点成为多个连通图,则输出-1.
思路:
就是用tarjan算法算出桥,再比较哪一个的值最小。
Tips:
注意三点:
①. 有重边,所以tarjan算法要处理重边。有两种处理方法,一种是先把所有的边存下,发现两点有重边的时候就只给这两个点连一条权值为无穷大的边。或者是在tarjan算法里处理重边,即使之求u或u的子树能够追溯到的最早的栈中节点的次序号时可访问父节点的次序号。
②. 如果无向图图本身已经有两个连通图了,就无需派人去炸桥,这时候输出0。
③. 如果求出来的最小权值桥的守卫人数为0时,也需要派出一个人去炸桥。
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
using namespace std;
const int MAXN = 1005;
struct Edge
{
int v, w, nxt;
Edge(){}
Edge(int _v, int _w, int _nxt):v(_v), w(_w), nxt(_nxt){}
}edge[MAXN*MAXN];
int n, m, cnt, sum, dep, top, scc;
int head[MAXN], dfn[MAXN], low[MAXN], belong[MAXN], Stack[MAXN];
void addedge(int u, int v, int w)
{
edge[cnt] = Edge(v, w, head[u]);
head[u] = cnt++;
edge[cnt] = Edge(u, w, head[v]);
head[v] = cnt++;
}
void Tarjan(int u, int fa)
{
dfn[u] = low[u] = ++dep;
Stack[top++] = u;
int tol = 0;
for(int i = head[u]; i != -1; i = edge[i].nxt)
{
int v = edge[i].v;
if(!dfn[v])
{
sum++;
Tarjan(v, u);
low[u] = min(low[u], low[v]);
}
else if(fa == v)
{
if(tol)
low[u] = min(low[u], dfn[v]);
tol++;
}
else
low[u] = min(low[u], dfn[v]);
}
if(dfn[u] == low[u])
{
int x;
scc++;
do
{
x = Stack[--top];
belong[x] = scc;
}while(x != u);
}
}
int main()
{
while(scanf("%d%d", &n, &m) != EOF && (n || m))
{
scc = top = dep = cnt = 0;
memset(dfn, 0, sizeof(dfn));
memset(low, 0, sizeof(low));
memset(head, -1, sizeof(head));
while(m--)
{
int u, v, w;
scanf("%d%d%d", &u, &v, &w);
addedge(u, v, w);
}
sum = 1;
Tarjan(1, 1);
int ans = 10010;
if(sum < n)
puts("0");
else
{
for(int i = 1; i <= n; i++)
for(int j = head[i]; j != -1; j = edge[j].nxt)
{
int v = edge[j].v;
if(belong[i] != belong[v])
ans = min(ans, edge[j].w);
}
if(ans == 10010)
puts("-1");
else if(ans == 0)
puts("1");
else
printf("%d\n", ans);
}
}
return 0;
}