caoao's bridges-tarjan算法求割边

Caocao was defeated by Zhuge Liang and Zhou Yu in the battle of Chibi. But he wouldn’t give up. Caocao’s army still was not good at water battles, so he came up with another idea. He built many islands in the Changjiang river, and based on those islands, Caocao’s army could easily attack Zhou Yu’s troop. Caocao also built bridges connecting islands. If all islands were connected by bridges, Caocao’s army could be deployed very conveniently among those islands. Zhou Yu couldn’t stand with that, so he wanted to destroy some Caocao’s bridges so one or more islands would be seperated from other islands. But Zhou Yu had only one bomb which was left by Zhuge Liang, so he could only destroy one bridge. Zhou Yu must send someone carrying the bomb to destroy the bridge. There might be guards on bridges. The soldier number of the bombing team couldn’t be less than the guard number of a bridge, or the mission would fail. Please figure out as least how many soldiers Zhou Yu have to sent to complete the island seperating mission.
Input
There are no more than 12 test cases.

In each test case:

The first line contains two integers, N and M, meaning that there are N islands and M bridges. All the islands are numbered from 1 to N. ( 2 <= N <= 1000, 0 < M <= N 2 )

Next M lines describes M bridges. Each line contains three integers U,V and W, meaning that there is a bridge connecting island U and island V, and there are W guards on that bridge. ( U ≠ V and 0 <= W <= 10,000 )

The input ends with N = 0 and M = 0.
Output
For each test case, print the minimum soldier number Zhou Yu had to send to complete the mission. If Zhou Yu couldn’t succeed any way, print -1 instead.
Sample Input
3 3
1 2 7
2 3 4
3 1 4
3 2
1 2 7
2 3 4
0 0
Sample Output
-1
4

思路
题意就是输入n和m,n表示有n个岛屿,m表示在这n个岛屿之间有m个桥,接下来输入m个桥的信息,u,v,w;w表示桥上守卫的士兵。caocao想要炸掉其中一个桥使图的连通分量大于1并且要派遣的士兵数量最少,就是求一条权值最小的割边。
暴力求割边肯定不行,这时就用到tarjan算法
算法参考自tarjan算法详解
好不容易看懂了算法,算法会了这道题还不算完,因为输入数据会有重边要判重;而且他会出现本身就是不连通不用炸桥的情况,这时answer等于0;如果算完之后那条割边没有士兵守护也要派遣一名,此时answer等于1

#include
#include
#define maxn 1010
#define maxe 1000005
#define inf 0x7fffffff
using namespace std;
struct edge{
	int v,next,we,id,flag;//v邻接的点,next下一个邻接的的点,we权值,id边的编号,flag标记是否有重边
}vec[maxe];
int head[maxn],dfn[maxn],low[maxn];//三个数组的含义见上面链接参考
int cutEdgeWe[maxe],vis[maxn];//cutEdgeWe存储割边的权值
int timeOrder,total,ans;//timeOrder时间戳
int init()
{
	memset(head,-1,sizeof(head));
	memset(dfn,0,sizeof(dfn));
	memset(low,0,sizeof(low));
	memset(vis,0,sizeof(vis));
	timeOrder=total=ans=0;
}
int add(int u,int v,int w,int id)
{
	for(int i=head[u];i!=-1;i=vec[i].next)//枚举与u邻接的点
	{
		if(vec[i].v==v)//如果与u邻接的点等于v,就说明已经有了u-v这条边 
		{
			vec[i].flag++;//重复率++ 
			return 1;//直接return 不用再加这条边了 
		}
	}
	vec[total].flag=0;//flag=0说明这条边没有重边
	vec[total].next=head[u];
	vec[total].v=v;
	vec[total].we=w;
	vec[total].id=id;
	head[u]=total++;
}
void tarjan(int x,int pre)
{
	low[x]=dfn[x]=++timeOrder;//按照时间戳 
	vis[x]=1;
	int v;
	for(int j=head[x];j!=-1;j=vec[j].next)
	{
		v=vec[j].v;
		if(v==pre)//不能让他又走到父节点pre 
			continue;
		if(!vis[v])//如果这个点没走过就继续dfs 
		{
			tarjan(v,x);
			low[x]=min(low[x],low[v]);
			if(dfn[x]<low[v]&&!vec[j].flag)//dfn[x]
				cutEdgeWe[ans++]=vec[j].we;//那么x-v这条边就是割边 
		}
		else
			low[x]=min(low[x],dfn[v]);
	}
}
int main()
{
	int n,m,u,v,w;
	while(scanf("%d %d",&n,&m)!=EOF&&(n+m))
	{
		init();
		for(int i=0;i<m;i++)
		{
			scanf("%d %d %d",&u,&v,&w);
			add(u,v,w,i);
			add(v,u,w,i);
		}
		tarjan(1,-1);
		int notVis=0;
		for(int i=1;i<=n;i++)
		{
			if(vis[i]==0)
				notVis++;
		}
		if(notVis)//如果有没走过的点,就说明图本身就不连通
		{
			printf("0\n");
			continue;
		}
		int minn=inf;
		for(int i=0;i<ans;i++)
			minn=min(minn,cutEdgeWe[i]);
		if(minn==0)//割边最小权值为0时也要派遣一个人
			printf("1\n");
		else if(minn==inf)//没有割边的情况
			printf("-1\n");
		else
			printf("%d\n",minn);			
	}
} 

你可能感兴趣的:(dfs,tarjan,dfs)