开始想用更简单的方法但是没实现,只能用了二维数组
无向图求桥的重点就是边(u,v)(在dfs时的父子边)如果是桥的话有dfn[u]<low[v]
求割点是:(非本题但就是想写了XD)
如果点u是dfs时的根,u至少有两个子节点(当然总结点数要大于3)那他就是割点
如果不是根,有一个子节点v满足dfn[u]>=low[v],比较好理解。
其中dfn是访问次序,low是所在连通分量的最小dfn
还是挺坑的...(gw,gw.jpg)
1.如果开始不连通就不用派人去
2.如果连通但是桥上没有人还要派一个人去,其他情况下相等即可(所以一直在wa)
3.这玩意可能是双向的,双向边不算桥...
代码如下:
#include<iostream>
#include<vector>
#include<stack>
#include<algorithm>
#include<string.h>
using namespace std;
const int maxn = 1005;
typedef vector<int> edge;
vector<edge>G(maxn);
int soilder[maxn][maxn] = {};//觉得麻烦就用了二维数组...
int bridgenum[maxn][maxn] = {};//如果双向就不算桥了...还会有双向...
int low[maxn] = {}, dfn[maxn] = {};
int tim;
int ans;
int father[maxn] = {};
int N, M;//N island M bridge
void tarjan(int u,int fa)//fa是u在dfn中的父节点
{
father[u] = fa;
low[u] = dfn[u] = tim++;
for (int i = 0; i < G[u].size(); i++)
{
int v = G[u][i];
if (!dfn[v])
{
tarjan(v,u);//开始写反了...
low[u] = min(low[u], low[v]);
}
else if (v != fa)
{
low[u] = min(low[u], low[v]);//因为无向图到父节点也是有边的,所以不能修改父节点
}
}
}
bool cando()//是否能做到
{
bool flag = 0;
for (int i = 2; i <= N; i++)
{
if (father[i] == 0)//本来就不连通的不需要派人,输出0
{
ans = 0;
return true;
}
int v = father[i];
if (dfn[v] < low[i]&& bridgenum[v][i]==1)//,找到桥,是说儿子不会有链接到v之前的点
{
ans = min(ans, soilder[v][i]);
if (ans == 0)//如果桥上没人还要派一个人去放啊啊啊啊啊
{
ans = 1;
}
flag = true;
}
}
return flag;
}
void init()
{
ans = 99999999;//不让用inf
tim = 1;
memset(soilder, 0, sizeof(soilder));
memset(low, 0, sizeof(low));
memset(dfn, 0, sizeof(dfn));
memset(bridgenum, 0, sizeof(bridgenum));
memset(father, 0, sizeof(father));//发现G不能memset...
G = vector<edge>(maxn);
}
int main()
{
int u, v,w;//结点和兵数
while (1)
{
cin >> N >> M;
if (N == 0 && M == 0)
{
break;
}
init();//初始化
while (M--)
{
cin >> u >> v >> w;
G[v].push_back(u);
G[u].push_back(v);
soilder[u][v] = soilder[v][u] = w;
bridgenum[u][v]++;
bridgenum[v][u]++;
}
tarjan(1, 0);//首个结点的父亲是0
if (cando())
{
printf("%d\n", ans);
}
else
{
printf("-1\n");//炸不了
}
}
return 0;
}