[kuangbin带你飞]专题九 连通图 题解报告

来源:互联网 发布:土耳其历史 知乎 编辑:程序博客网 时间:2024/05/18 03:02
专题链接
关于tarjan

A - Network of Schools  原题地址

本题有2个问题,第一个是要算最少要给多少个点软件,才能使所有点都可以收到副本
第二个是要算最少加多少条边,使得图变成强连通

1:tarjan求强连通,然后缩点,计算入度为0的强连通分量
2:设现在有a个入度为0的点,b个出度为0的点(缩完点后的点),最合理的加边方法肯定是从出度为0的点向入度为0的点添加有向边,
如果a > b, 添加a条边,所有点的入度都大于0,所有点的出度也大于0,问题解决,答案是a
如果 a <= b,添加a条边,所有点入度大于0,但是还有b-a个点,它们的出度是0,所以还要再加b-a条边,所以答案是b
综合两种情况,答案是max(a,b)

当然如果图原来就是强连通的话,输出就是1 和 0 了

#include <map>#include <set>#include <queue>#include <stack>#include <vector>#include <cmath>#include <cstdio>#include <cstdlib>#include <cstring>#include <iostream>#include <algorithm>using namespace std;const int N = 110;struct node{int next;int to;}edge[N * N];int st[N];int tot, ord, sccnum, top;int head[N];int low[N];int DFN[N];int in_deg[N];int out_deg[N];int block[N];bool instack[N];void addedge(int from, int to){edge[tot].to = to;edge[tot].next = head[from];head[from] = tot++;}void tarjan (int u){ DFN[u] = low[u] = ++ord; instack[u] = 1; st[top++] = u; for (int i = head[u]; ~i; i = edge[i].next) {  int v = edge[i].to;  if (DFN[v] == -1) {  tarjan (v);  if (low[u] > low[v]) {  low[u] = low[v]; } } else if (instack[v]) {  low[u] = min(low[u], DFN[v]); } } int v; if (DFN[u] == low[u]) {  sccnum++;  do {v = st[--top];block[v] = sccnum;instack[v] = 0; }while (v != u); }}void init (){memset (DFN, -1, sizeof(DFN));memset (low, 0, sizeof(low));memset (head, -1, sizeof(head));memset (in_deg, 0, sizeof(in_deg));memset (out_deg, 0, sizeof(out_deg));memset (instack, 0, sizeof(instack));top = 0;tot = 0;sccnum = 0;ord = 0;}void solve (int n){for (int i = 1; i <= n; ++i){if (DFN[i] == -1){tarjan (i);}}if (sccnum == 1){printf("1\n0\n");return;}for (int u = 1; u <= n; ++u){for (int j = head[u]; ~j; j = edge[j].next){int v = edge[j].to;if (block[u] == block[v]){continue;}out_deg[block[u]]++;in_deg[block[v]]++;}}int a, b;a = 0;b = 0;for (int i = 1; i <= sccnum; ++i){if (in_deg[i] == 0){a++;}else if (out_deg[i] == 0){b++;}}printf("%d\n", a);printf("%d\n", max(a, b));}int main(){int n;int u, v;while (~scanf("%d", &n)){init();for (int u = 1; u <= n; ++u){while (scanf("%d", &v), v){addedge (u, v);}}solve(n);}return 0;}

B - Network  原题地址

求割点数目,模版题

#include <map>#include <set>#include <queue>#include <stack>#include <vector>#include <cmath>#include <cstdio>#include <cstdlib>#include <cstring>#include <iostream>#include <algorithm>using namespace std;const int N = 110;struct node{int next;int to;}edge[N * N];bool instack[N];bool cut[N];int head[N];int DFN[N];int low[N];int cnt, tot, ord, root;void addedge (int from, int to){edge[tot].to = to;edge[tot].next = head[from];head[from] = tot++;}void tarjan (int u, int fa){DFN[u] = low[u] = ++ord;instack[u] = 1;int cnt = 0;for (int i = head[u]; ~i; i = edge[i].next){int v = edge[i].to;if (v == fa){continue;}if (DFN[v] == -1){tarjan(v, u);cnt++;if (low[u] > low[v]){low[u] = low[v];}if (root == u && cnt > 1){cut[u] = 1;}else if (u != root && low[v] >= DFN[u]){cut[u] = 1;}}else if (instack[v]){low[u] = min(low[u], DFN[v]);}}}void init (){memset (DFN, -1, sizeof(DFN));memset (low, 0, sizeof(low));memset (instack, 0, sizeof(instack));memset (cut, 0, sizeof(cut));memset (head, -1, sizeof(head));tot = 0;ord = 0;}void solve (int n){root = 1;tarjan (1, -1);int ans = 0;for (int i = 1; i <= n; ++i){if (cut[i]){ans++;}}printf("%d\n", ans);}int main(){int n;int u, v;while (~scanf("%d", &n), n){init();while (scanf("%d", &u), u){while (getchar() != '\n'){scanf("%d", &v);addedge (u, v);addedge (v, u);}}solve(n);}return 0;}

C - Critical Links

求桥,模版题,这题似乎不用判重边

#include <map>#include <set>#include <queue>#include <stack>#include <vector>#include <cmath>#include <cstdio>#include <cstdlib>#include <cstring>#include <iostream>#include <algorithm>using namespace std;const int N = 100010;const int M = (N << 2);struct node{int next;int to;//int id;}edge[M];struct BRIDGE{int u;int v;}bridge[M];int head[N];int DFN[N];int low[N];int st[N];bool instack[N];int tot, top, ord, sccnum, cnt;void init (){memset (head, -1, sizeof(head));memset (DFN, -1, sizeof(DFN));memset (low, 0, sizeof(low));memset (instack, 0, sizeof(instack));tot = top = cnt = sccnum = ord = 0;}void addedge (int from, int to){edge[tot].to = to;//edge[tot].id = id;edge[tot].next = head[from];head[from] = tot++;}int cmp (BRIDGE a, BRIDGE b){if (a.u == b.u){return a.v < b.v;}return a.u < b.u;}void tarjan (int u, int fa){DFN[u] = low[u] = ++ord;instack[u] = 1;st[top++] = u;for (int i = head[u]; ~i; i = edge[i].next){int v = edge[i].to;if (v == fa){continue;}if (DFN[v] == -1){tarjan (v, u);low[u] = min (low[v], low[u]);if (low[v] > DFN[u]){bridge[++cnt].u = u;bridge[cnt].v = v;if (bridge[cnt].u > bridge[cnt].v){swap (bridge[cnt].u, bridge[cnt].v);}}}else if (instack[v]){low[u] = min (low[u], DFN[v]);}}if (low[u] == DFN[u]){++sccnum;int v;do{v = st[--top];instack[v] = 0;}while (u != v);}}void solve (int n){for (int i = 1; i <= n; ++i){if (DFN[i] == -1){tarjan(i, -1);}}sort (bridge + 1, bridge + cnt + 1, cmp);printf("%d critical links\n", cnt);for (int i = 1; i <= cnt; ++i){printf("%d - %d\n", bridge[i].u - 1, bridge[i].v - 1);}printf("\n");}int main(){int n;int u, v;int num;while (~scanf("%d", &n)){if (n == 0){printf("0 critical links\n\n");continue;}init();for (int i = 1; i <= n; ++i){scanf("%d", &u);++u;getchar();getchar();scanf("%d", &num);getchar();while (num--){scanf("%d", &v);++v;addedge (u, v);}}solve (n);}return 0;}

D - Network  原题地址
敢不敢不叫 Network了

询问每次添加一条边以后剩下的桥的数目
先一次tarjan缩点,得到一颗树,每次 加边,两个端点到它们的lca之间的边都不再是桥,所以每一次我们都可以通过暴力求出lca,然后统计出少了多少条桥,但是暴力统计时,会遇到某些边在之前就不是桥的情况,我们用并查集来跳过这些边(每一次加边就把lca路径上的点都合并到一个集合里去,这里根用最上面的点,到时如果遇到这种点,直接可以跳到它们的根上去)

#include <map>#include <set>#include <queue>#include <stack>#include <vector>#include <cmath>#include <cstdio>#include <cstdlib>#include <cstring>#include <iostream>#include <algorithm>#pragma comment(linker, "/STACK:1024000000,1024000000")using namespace std;const int N = 110010;const int M = 210010;int head[N];int head2[N];int DFN[N];int low[N];int deep[N];int father[N];int block[N];int pre[N];int st[N];bool instack[N];int tot, tot2, top, ord, sccnum, icase;struct node{int next;int to;int id;}edge[M << 2], edge2[M << 2];void init (){memset (DFN, -1, sizeof(DFN));memset (head, -1, sizeof(head));memset (head2, -1, sizeof(head2));memset (low, 0, sizeof(low));memset (instack, 0, sizeof(instack));tot = tot2 = ord = sccnum = top = 0;}void addedge (int from, int to, int id){edge[tot].to = to;edge[tot].id = id;edge[tot].next = head[from];head[from] = tot++;}void addedge2 (int from, int to){edge2[tot2].to = to;edge2[tot2].next = head2[from];head2[from ] = tot2++;}void dfs (int u, int fa, int d){pre[u] = fa;deep[u] = d;for (int i = head2[u]; ~i; i = edge2[i].next){int v = edge2[i].to;if (v == fa){continue;}dfs (v, u, d + 1);}}int find (int x){if (x == father[x]){return x;}return father[x] = find(father[x]);}void tarjan (int u, int x){DFN[u] = low[u] = ++ord;instack[u] = 1;st[top++] = u;for (int i = head[u]; ~i; i = edge[i].next){int v = edge[i].to;if (edge[i].id == x){continue;}if (DFN[v] == -1){tarjan (v, edge[i].id);low[u] = min(low[u], low[v]);}else if (instack[v]){low[u] = min(low[u], DFN[v]);}}int v;if (DFN[u] == low[u]){++sccnum;do{v = st[--top];instack[v] = 0;block[v] = sccnum;}while (v != u);}}int LCA (int a, int b){while (a != b){if (deep[a] > deep[b]){a = pre[a];}else if (deep[a] < deep[b]){b = pre[b];}else{a = pre[a];b = pre[b];}a = find(a);b = find(b);}return a;}void solve (int n){tarjan (1, -1);for (int u = 1; u <= n; ++u){for (int i = head[u]; ~i; i = edge[i].next){int v = edge[i].to;if (block[u] == block[v]){continue;}addedge2 (block[u], block[v]);}}for (int i = 1; i <= sccnum; ++i){father[i] = i;}int cnt = sccnum - 1;dfs(1, -1, 0);int q, a, b, lca;scanf("%d", &q);printf("Case %d:\n", icase++);while (q--){scanf("%d%d", &a, &b);a = block[a];b = block[b];if (a == b){printf("%d\n", cnt);continue;}a = find(a);b = find(b);lca = LCA (a, b);int x = 0;while (a != lca){++x;father[a] = lca;a = pre[a];a = find(a);}while (b != lca){++x;father[b] = lca;b = pre[b];b = find(b);}cnt -= x;printf("%d\n", cnt);}}int main(){int n, m;int u, v;icase = 1;while (~scanf("%d%d", &n, &m)){if (!n && !m){break;}init();for (int i = 1; i <= m; ++i){scanf("%d%d", &u, &v);addedge (u, v, i);addedge (v, u, i);}solve(n);printf("\n");}return 0;}


E - Redundant Paths 原题地址

求桥的数目,模版题,注意重边的判定

#include <map>#include <set>#include <queue>#include <stack>#include <vector>#include <cmath>#include <cstdio>#include <cstdlib>#include <cstring>#include <iostream>#include <algorithm>using namespace std;const int N = 5010;const int M = 10010;struct node{int next;int to;int id;}edge[M << 1];int head[N];int DFN[N];int low[N];bool instack[N];int block[N];int deg[N];stack <int> st;int tot, sccnum, ord;void addedge (int from, int to, int id){edge[tot].id = id;edge[tot].to = to;edge[tot].next = head[from];head[from] = tot++;}void init (){memset (DFN, -1, sizeof(DFN));memset (low, 0, sizeof(low));memset (instack, 0, sizeof(instack));memset (deg, 0, sizeof(deg));memset (head, -1, sizeof(head));while (!st.empty()){st.pop();}tot = sccnum = ord = 0;}void tarjan (int u, int x){DFN[u] = low[u] = ++ord;instack[u] = 1;st.push(u);for (int i = head[u]; ~i; i = edge[i].next){int v = edge[i].to;if (x == edge[i].id){continue;}if (DFN[v] == -1){tarjan (v, edge[i].id);low[u] = min(low[u], low[v]);}else if (instack[v]){low[u] = min(low[u], DFN[v]);}}int v;if (DFN[u] == low[u]){++sccnum;do{v = st.top();st.pop();instack[v] = 0;block[v] = sccnum;}while (v != u);}}void solve (int n){for (int i = 1; i <= n; ++i){if (DFN[i] == -1){tarjan (i, -1);}}for (int u = 1; u <= n; ++u){for (int j = head[u]; ~j; j = edge[j].next){int v = edge[j].to;if (block[u] == block[v]){continue;}    ++deg[block[u]];    ++deg[block[v]];}}int cnt = 0;for (int i = 1; i <= sccnum; ++i){if (deg[i] / 2 == 1){++cnt;}}printf("%d\n", (cnt + 1) >> 1);}int main(){int n, m;int u, v;while (~scanf("%d%d", &n, &m)){init();for (int i = 1; i <= m; ++i){scanf("%d%d", &u, &v);addedge (u, v, i);addedge (v, u, i);}solve(n);}return 0;}



F - Warm up 原题地址

询问如何加一条边,使得剩下的桥的数目最少,输出数目
我的做法是先tarjan缩点,得到树,然后求出树的直径,把边加在直径的两端,减少的桥肯定是最多的

#include <map>#include <set>#include <list>#include <stack>#include <vector>#include <queue>#include <cmath>#include <cstdio>#include <cstring>#include <iostream>#include <algorithm>#pragma comment(linker, "/STACK:1024000000,1024000000")using namespace std;const int N = 200010;const int inf = 0x3f3f3f3f;const int M = 3000010;struct node{    int next;    int to;    int id;}edge[M], edge2[M];bool vis[N];bool flag[N];int dist[N];int head[N];int head2[N];int low[N];int block[N];int DFN[N];bool instack[N];stack<int>qu;int br_cnt, n, m, end_p;int tot, tot2, maxs, index, top, sccnum;void addedge(int from, int to, int id){    edge[tot].id = id;    edge[tot].to = to;    edge[tot].next = head[from];    head[from] = tot++;}void addedge2(int from, int to){    edge2[tot].to = to;    edge2[tot].next = head2[from];    head2[from] = tot++;}void init(){    index = 0;    sccnum = 0;    top = 0;    tot = 0;    tot2 = 0;    maxs = 0;    br_cnt = 0;    end_p = 0;    memset ( head, -1, sizeof(head) );    memset (head2, -1, sizeof(head2) );    memset ( low, 0, sizeof(low) );    memset (DFN, 0, sizeof(DFN) );    memset ( instack, 0, sizeof(instack) );    memset ( flag, 0, sizeof(flag) );    memset (vis, 0, sizeof(vis) );}void build(){    int u, v;    while (!qu.empty() )    {        qu.pop();    }    for (int i = 1; i <= m; ++i)    {        scanf("%d%d", &u, &v);        addedge(u, v, i);        addedge(v, u, i);    }}void tarjan(int u, int fa)  {      qu.push(u);    instack[u] = 1;    DFN[u] = low[u] = ++index;      for (int i = head[u]; i != -1; i = edge[i].next)      {          int v = edge[i].to;          if (fa == edge[i].id)          {            continue;        }           if ( !instack[v] )          {              tarjan(v, edge[i].id);              low[u] = min(low[u], low[v]);              if (DFN[u] < low[v])            {                br_cnt++;            }        }          else         {            low[u] = min(DFN[v], low[u]);        }      }    if (DFN[u] == low[u])    {        sccnum++;        int v;        while(1)        {            v = qu.top();            qu.pop();            instack[v] = 0;            block[v] = sccnum;            if(v == u)            {                break;            }        }    }}void bfs(int s){    queue<int>qu;    while ( !qu.empty() )    {        qu.pop();    }    memset ( vis, 0, sizeof(vis) );    qu.push(s);    dist[s] = 0;    vis[s] = 1;    maxs = 0;    while ( !qu.empty() )    {        int u = qu.front();        qu.pop();        for (int i = head2[u]; i != -1; i = edge2[i].next)        {            int v = edge2[i].to;            if (!vis[v])            {                vis[v] = 1;                dist[v] = dist[u] + 1;                qu.push(v);                if (maxs < dist[v])                {                    maxs = dist[v];                    end_p = v;                }            }        }    }}void solve(){    for (int i = 1; i <= n; ++i)    {        if (DFN[i] == 0)        {            tarjan(i, -1);        }    }     for (int u = 1; u <= n; ++u)    {        for (int i = head[u]; i != -1; i = edge[i].next)        {            int v = edge[i].to;            if (block[u] != block[v])            {                addedge2(block[u], block[v]);            }        }    }    bfs(1);    bfs(end_p);    printf("%d\n", br_cnt - maxs);}int main(){    while (~scanf("%d%d", &n, &m))    {        if (!n && !m)        {            break;        }        init();        build();        solve();    }    return 0;}

G - Strongly connected 原题地址

求最多可以加多少边,使得最新的图不是强连通图
最终情况一定是再加一条边,整张图就是强连通的了,那么我们可以把图看成2部分x和y,x和y都是完全图,然后x每个点到y每个点都有边,但是y到x没有边,如果有肯定是强连通了,设x中有a个点,y中有b个点 则 a + b = n
则边数就是 a * (a - 1) + b * (b - 1) + a * b - m,化简得到:n * n - a * b - n - m;
如何让这个值大那就是如何选取x和y的问题了,显然a和b差距越大越好,那么就可以通过tarajan来找出一个包含点最少的强连通分量来当x,其他的强连通分量作为y,这样就很容易了

#include <map>#include <set>#include <queue>#include <stack>#include <vector>#include <cmath>#include <cstdio>#include <cstdlib>#include <cstring>#include <iostream>#include <algorithm>using namespace std;const int N = 100010;struct node{int next;int to;}edge[N << 1];int head[N];int DFN[N];int low[N];int num[N];int st[N];bool instack[N];int block[N];int in[N];int out[N];int tot, top, ord, sccnum;void addedge (int from, int to){edge[tot].to = to;edge[tot].next = head[from];head[from] = tot++;}void init (){memset (num, 0, sizeof(num));memset (in, 0, sizeof(in));memset (out, 0, sizeof(out));memset (head, -1, sizeof(head));memset (DFN, -1, sizeof(DFN));memset (low, 0, sizeof(low));memset (instack, 0, sizeof(instack));tot = top = ord = sccnum = 0;}void tarjan (int u){DFN[u] = low[u] = ++ord;instack[u] = 1;st[top++] = u;for (int i = head[u]; ~i; i = edge[i].next){int v = edge[i].to;if (DFN[v] == -1){tarjan (v);low[u] = min (low[u], low[v]);}else if (instack[v]){low[u] = min(low[u], DFN[v]);}}if (DFN[u] == low[u]){int v;++sccnum;do{++num[sccnum];v = st[--top];block[v] = sccnum;instack[v] = 0;}while (v != u);}}void solve (int n, int m){for (int i = 1; i <= n; ++i){if (DFN[i] == -1){tarjan (i);}}if (sccnum == 1){printf("-1\n");return;}for (int u = 1; u <= n; ++u){for (int i = head[u]; ~i; i = edge[i].next){int v = edge[i].to;if (block[u] == block[v]){continue;}++out[block[u]];++in[block[v]];}}long long ans = 0;long long ret = (long long)(n * n - n - m);for (int i = 1; i <= sccnum; ++i){if (in[i] == 0 || out[i] == 0){ans = max(ans, ret - (long long)(num[i] * (n - num[i])));}}printf("%lld\n", ans);}int main(){int t;int n, m;int u, v;int icase = 1;scanf("%d", &t);while (t--){scanf("%d%d", &n, &m);init();for (int i = 1; i <= m; ++i){scanf("%d%d", &u, &v);addedge (u, v);}printf("Case %d: ", icase++);solve (n, m);}return 0;}


H - Prince and Princess 原题地址

首先做一次最大匹配,设为cnt,那么对于左边,有n-cnt个王子没有匹配,对于右边,有m-cnt个公主没有匹配,所以我们在左边加上m-cnt个虚拟点,这些点喜欢所有公主,右边加上n-cnt个虚拟点,这些点被所有王子喜欢,这样左右两边都是n+m-cnt个点,在求一次最大匹配,这一定是一个完备匹配,对于每一个王子,用他目前匹配的公主,向所有他喜欢的公主连一条有向边,这表示单方面可以替换,所以再对得到的新图求强连通,处在一个强连通分量的公主可以相互替换

#include <map>#include <set>#include <queue>#include <stack>#include <vector>#include <cmath>#include <cstdio>#include <cstdlib>#include <cstring>#include <iostream>#include <algorithm>using namespace std;const int N = 1100;int head[N];int DFN[N];int low[N];int block[N];int match[N];int match2[N];bool used[N];bool instack[N];bool g[N][N];stack <int> st;vector <int> vis;int n, m, tot, sccnum, ord, num_v, num_u;struct node{int next;int to;}edge[N * N * 5];void init (){vis.clear();memset (g, 0, sizeof(g));memset (match, -1, sizeof(match));memset (match2, -1, sizeof(match2));memset (used, 0, sizeof(used));memset (instack, 0, sizeof(instack));memset (DFN, -1, sizeof(DFN));memset (low, 0, sizeof(low));memset (head, -1, sizeof(head));tot = sccnum = ord = 0;}void addedge (int from, int to){edge[tot].to = to;edge[tot].next = head[from];head[from] = tot++;}bool dfs (int u){for (int i = 1; i <= num_v; ++i){if (!g[u][i]){continue;}int v = i;if (!used[v]){used[v] = 1;if (match[v] == -1 || dfs (match[v])){match[v] = u;return 1;}}}return 0;}int hungry (){int cnt = 0;for (int i = 1; i <= num_u; ++i){memset (used, 0, sizeof(used));if (dfs(i)){++cnt;}}return cnt;}void tarjan (int u){DFN[u] = low[u] = ++ord;instack[u] = 1;st.push(u);for (int i = head[u]; ~i; i = edge[i].next){int v = edge[i].to;if (DFN[v] == -1){tarjan (v);low[u] = min (low[v], low[u]);}else if (instack[v]){low[u] = min (low[u], DFN[v]);}}int v;if (low[u] == DFN[u]){++sccnum;do{v = st.top();st.pop();instack[v] = 0;block[v] = sccnum;}while (u != v);}}void solve (){num_u = n;num_v = m;int cnt = hungry();num_u = n + m - cnt;num_v = num_u;for (int i = n + 1; i <= num_u; ++i){for (int j = 1; j <= num_v; ++j){g[i][j] = 1;}}for (int i = 1; i <= num_u; ++i){for (int j = m + 1; j <= num_v; ++j){g[i][j] = 1;}}//printf("%d\n", cnt);memset (match, -1, sizeof(match));cnt = hungry();//printf("%d\n", cnt);for (int i = 1; i <= num_v; ++i){match2[match[i]] = i;}for (int i = 1; i <= num_u; ++i){for (int j = 1; j <= num_v; ++j){if (g[i][j] && match2[i] != j){addedge (match2[i], j);}}}for (int i = 1; i <= num_v; ++i){if (DFN[i] == -1){tarjan (i);}}for (int i = 1; i <= n; ++i){vis.clear();for (int j = 1; j <= m; ++j){if (g[i][j] && block[j] == block[match2[i]]){vis.push_back (j);}}int tmp = vis.size();printf("%d", tmp);for (int j = 0; j < tmp; ++j){printf(" %d", vis[j]);}printf("\n");}}int main (){int t;int u, v, num;int icase = 1;scanf("%d", &t);while (t--){scanf("%d%d", &n, &m);init();for (int i = 1; i <= n; ++i){scanf("%d", &num);while (num--){scanf("%d", &v);g[i][v] = 1;}}printf("Case #%d:\n", icase++);solve();}return 0;}


I - Caocao's Bridges  原题地址

此题其实不难,但是很坑
1:如果图不连通,那么不需要派人去
2:如果有一条桥,防卫兵数目为0,那么应该派1个人去
3:重边判定

#include <map>#include <set>#include <queue>#include <stack>#include <vector>#include <cmath>#include <cstdio>#include <cstdlib>#include <cstring>#include <iostream>#include <algorithm>using namespace std;const int N = 1010;const int M = N * N;struct node{int next;int to;int id;int w;}edge[M << 1];int head[N];int DFN[N];int low[N];int st[N];bool instack[N];int bridge[M];int tot, cnt, ord, top, sccnum;void addedge (int from, int to, int id, int w){edge[tot].w = w;edge[tot].to = to;edge[tot].id = id;edge[tot].next = head[from];head[from] = tot++;}void init (){memset (head, -1, sizeof(head));memset (DFN, -1, sizeof(DFN));memset (low, 0, sizeof(low));memset (instack, 0, sizeof(instack));tot = cnt = ord = top = sccnum = 0;}void tarjan (int u, int x){DFN[u] = low[u] = ++ord;instack[u] = 1;st[top++] = u;for (int i = head[u]; ~i; i = edge[i].next){int v = edge[i].to;if (edge[i].id == x){continue;}if (DFN[v] == -1){tarjan (v, edge[i].id);low[u] = min(low[u], low[v]);if (low[v] > DFN[u]){bridge[++cnt] = edge[i].w;}}else if (instack[v]){low[u] = min(low[u], DFN[v]);}}int v;if (DFN[u] == low[u]){++sccnum;do{v = st[--top];instack[v] = 0;}while (u != v);}}void solve (int n){int num = 0;for (int i = 1; i <= n; ++i){if (DFN[i] == -1){++num;tarjan (i, -1);}if (num > 1){break;}}if (num > 1){printf("0\n");return;}if (cnt == 0){printf("-1\n");return;}int minx = 0x3f3f3f3f;for (int i = 1; i <= cnt; ++i){if (minx > bridge[i]){minx = bridge[i];}}if (minx == 0){++minx;}printf("%d\n", minx);}int main(){int n, m;int u, v, w;while (~scanf("%d%d", &n, &m)){if (!n && !m){break;}init();for (int i = 1; i <= m; ++i){scanf("%d%d%d", &u, &v, &w);addedge (u, v, i, w);addedge (v, u, i, w);}solve(n);}return 0;}


1 0