F - Caocao'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
去重边:

对于无向图而言,网上主要两种写法,

第一种给每一条边的正反方向都分配一个相同但唯一的id,那么在判断时如果两条边的id是一样的,就说明是一条边,这条边就不要走了。

在tarjan算法中,对于这种写法,传入参数就要有两个(出发点u,到达u的边的id)


void Tarjan(int u,int id){ //当前点u,来到u的边的id
    low[u] = dfn[u] = ++cnt; //给定时间戳
    for(int i=first[u];i!=-1;i=edge[i].next){
        int v = edge[i].to;
        if(id == edge[i].id){//id是相同,说明是同一条边,不要走
            continue;
        }
        if(!dfn[v]){
            Tarjan(v,edge[i].id);
            low[u] = min(low[u],low[v]);
            if(low[v] > dfn[u]){
                if(edge[i].w < ans){
                    ans = edge[i].w;
                }
            }
        }
        else{
            low[u] = min(low[u],dfn[v]);
        }
    }
}

第二种,如果有重边,对于两点而言,在邻接表中存储重边的先后顺序起码是一致的(即如果重边有两条,那么这两条边在两点的邻接表中存储顺序都是先一后二),所以说,利用这个特点和递归的性质,遇到的第一条边肯定是同一条边,不要走;如果再出现就说明有重边。

对于这种情况,Tarjan算法就要知道当前点和它的父亲节点才行,也就是传入参数改为当前点u和父节点f。


void Tarjan(int u,int f){
    low[u] = dfn[u] = ++cnt;
    int flag = 0; //标记是否出现重边
    for(int i=first[u];i!=-1;i=edge[i].next){
        int v = edge[i].to;
        if(v == f && !flag){ //同一条边,不要走
            flag = 1; 
            continue;
        }
        if(!dfn[v]){
            Tarjan(v,u);
            low[u] = min(low[u],low[v]);
            if(low[v] > dfn[u]){
                if(edge[i].w < ans){
                    ans = edge[i].w;
                }
            }
        }
        else{
            low[u] = min(low[u],dfn[v]);
        }
    }
}

两种方法任意用其中一个。

AC完整代码
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#define MAXN 1004
#define MOD 1000000009
#define INF 0x7ffffff

using namespace std;


struct EDGE{
    int next;
    int to;
    int w;
    int id;
}edge[MAXN*MAXN];

int n,m;
int low[MAXN];
int dfn[MAXN];
int first[MAXN];
int cnt;
int total;
int ans;

void init(){
    memset(low,0,sizeof(low));
    memset(dfn,0,sizeof(dfn));
    memset(first,-1,sizeof(first));
    cnt = total = 0;
    ans = INF;
}

inline void add(int u,int v,int w,int id){
    edge[total].to = v;
    edge[total].w = w;
    edge[total].next = first[u];
    edge[total].id = id;
    first[u] = total++;
}

void Tarjan1(int u,int id){
    low[u] = dfn[u] = ++cnt;
    for(int i=first[u];i!=-1;i=edge[i].next){
        int v = edge[i].to;
        if(id == edge[i].id){
            continue;
        }
        if(!dfn[v]){
            Tarjan1(v,edge[i].id);
            low[u] = min(low[u],low[v]);
            if(low[v] > dfn[u]){
                if(edge[i].w < ans){
                    ans = edge[i].w;
                }
            }
        }
        else{
            low[u] = min(low[u],dfn[v]);
        }
    }
}


void Tarjan2(int u,int f){
    low[u] = dfn[u] = ++cnt;
    int flag = 0; //标记是否出现重边
    for(int i=first[u];i!=-1;i=edge[i].next){
        int v = edge[i].to;
        if(v == f && !flag){ //同一条边,不要走
            flag = 1;
            continue;
        }
        if(!dfn[v]){
            Tarjan2(v,u);
            low[u] = min(low[u],low[v]);
            if(low[v] > dfn[u]){
                if(edge[i].w < ans){
                    ans = edge[i].w;
                }
            }
        }
        else{
            low[u] = min(low[u],dfn[v]);
        }
    }
}


int main(){
    while(cin >> n >> m && (n+m)){
        init();
        int u,v,w;
        int id = 1;
        while(m--){
            cin >> u >> v >> w;
            add(u,v,w,id);
            add(v,u,w,id);
            ++id;
        }
        //Tarjan1(1,-1);//两种方法
        Tarjan2(1,1);
        if(cnt != n) cout << 0 << endl;
        else{
            if(ans == INF){
                cout << -1 << endl;
            }
            else cout << (ans == 0 ? 1 : ans) << endl;
        }
    }
    return 0;
}

你可能感兴趣的:(图论—强连通)