算法笔记_149:图论之桥的应用(Java)

目录

1 问题描述

2 解决方案

 


1 问题描述

1310 One-way traffic
In a certain town there are n intersections connected by two- and one-way streets. The town is very
modern so a lot of streets run through tunnels or viaducts. Of course it is possible to travel between
any two intersections in both ways, i.e. it is possible to travel from an intersection a to an intersection
b as well as from b to a without violating traffic rules. Because one-way streets are safer, it has been
decided to create as much one-way traffic as possible. In order not to make too much confusion it has
also been decided that the direction of traffic in already existing one-way streets should not be changed.
Your job is to create a new traffic system in the town. You have to determine the direction of
traffic for as many two-way streets as possible and make sure that it is still possible to travel both ways
between any two intersections.
Write a program that:
? reads a description of the street system in the town from the standard input,
? for each two-way street determines one direction of traffic or decides that the street must remain
two-way,
? writes the answer to the standard output.
Input
The first line of the input contains two integers n and m, where 2 ≤ n ≤ 2000 and n?1 ≤ m ≤ n(n?1)/2.
Integer n is the number of intersections in the town and integer m is the number of streets.
Each of the next m lines contains three integers a, b and c, where 1 ≤ a ≤ n, 1 ≤ b ≤ n, a ?= b and
c belongs to {1, 2}. If c = 1 then intersections a and b are connected by an one-way street from a to
b. If c = 2 then intersections a and b are connected by a two-way street. There is at most one street
connecting any two intersections.
Output
The output contains exactly the same number of lines as the number of two-way streets in the input.
For each such street (in any order) the program should write three integers a, b and c meaning, the new
direction of the street from a to b (c = 1) or that the street connecting a and b remains two-way (c = 2).
If there are more than one solution with maximal number of one-way streets then your program should
output any of them but just one.
Sample Input
4 4
4 1 1
4 2 2
1 2 1
1 3 2

Sample Output
2 4 1
3 1 2

题目链接:https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=4056


2 解决方案

首先看看关于图论的割点和桥的相关概念定义:

引用文末参考资料1:

1.割点:若删掉某点后,原连通图分裂为多个子图,则称该点为割点。

2.割点集合:在一个无向连通图中,如果有一个顶点集合,删除这个顶点集合,以及这个集合中所有顶点相关联的边以后,原图变成多个连通块,就称这个点集为割点集合。

3.点连通度:最小割点集合中的顶点数。

4.割边(桥):删掉它之后,图必然会分裂为两个或两个以上的子图。

5.割边集合:如果有一个边集合,删除这个边集合以后,原图变成多个连通块,就称这个点集为割边集合。

6.边连通度:一个图的边连通度的定义为,最小割边集合中的边数。

7.缩点:把没有割边的连通子图缩为一个点,此时满足任意两点之间都有两条路径可达。

注:求块<>求缩点。缩点后变成一棵k个点k-1条割边连接成的树。而割点可以存在于多个块中。

8.双连通分量:分为点双连通和边双连通。它的标准定义为:点连通度大于1的图称为点双连通图,边连通度大于1的图称为边双连通图。通俗地讲,满足任意两点之间,能通过两条或两条以上没有任何重复边的路到达的图称为双连通图。无向图G的极大双连通子图称为双连通分量。

具体代码如下:

package com.liuzhen.practice;

import java.util.ArrayList;
import java.util.Scanner;
import java.util.Stack;

public class Main {
    public static int n;  //给定图的顶点数
    public static int count;  //记录遍历次序
    public static int[] DFN;
    public static int[] Low;
    public static int[] parent;   //parent[i] = j,表示顶点i的直接父母顶点为j
    public static Stack<Integer> stack;
    public static ArrayList<edge>[] map;
    public static ArrayList<edge> ans;  //存储最终输出结果

    static class edge {
        public int a;  //边的起点
        public int b;  //边的终点
        public int c;  //c = 1表示单向边,c = 2表示双向边

        public edge(int a, int b, int c) {
            this.a = a;
            this.b = b;
            this.c = c;
        }
    }

    @SuppressWarnings("unchecked")
    public void init() {
        count = 0;
        DFN = new int[n + 1];
        Low = new int[n + 1];
        parent = new int[n + 1];
        stack = new Stack<Integer>();
        map = new ArrayList[n + 1];
        ans = new ArrayList<edge>();
        for(int i = 1;i <= n;i++) {
            DFN[i] = -1;
            Low[i] = -1;
            parent[i] = -1;
            map[i] = new ArrayList<edge>();
        }
    }

    public void TarJan(int start, int father) {
        DFN[start] = count++;
        Low[start] = DFN[start];
        parent[start] = father;
        stack.push(start);
        for(int i = 0;i < map[start].size();i++) {
            edge temp = map[start].get(i);
            int j = temp.b;
            if(DFN[j] == -1) {
                TarJan(j, start);
                Low[start] = Math.min(Low[start], Low[j]);
                if(temp.c == 2) {
                    if(Low[j] > DFN[start]) {   //当边temp为割边(或者桥)时
                        ans.add(temp);
                    } else {
                        ans.add(new edge(temp.a, temp.b, 1));
                    }
                }
            } else if(j != parent[start]) {  //当j不是start的直接父母节点时
                Low[start] = Math.min(Low[start], DFN[j]);
                if(temp.c == 2) {
                    ans.add(new edge(temp.a, temp.b, 1));
                }
            }
        }
    }

    public void getResult() {
        for(int i = 1;i <= n;i++) {
            if(parent[i] == -1)
                TarJan(i, 0);
        }
        for(int i = 0;i < ans.size();i++)
            System.out.println(ans.get(i).a+" "+ans.get(i).b+" "+ans.get(i).c);
    }

    public static void main(String[] args) {
        Main test = new Main();
        Scanner in = new Scanner(System.in);
        n = in.nextInt();
        int k = in.nextInt();
        test.init();
        for(int i = 0;i < k;i++) {
            int a = in.nextInt();
            int b = in.nextInt();
            int c = in.nextInt();
            map[a].add(new edge(a, b, c));
            if(c == 2)
                map[b].add(new edge(b, a, c));
        }
        test.getResult();
    }
}

运行结果:

4 4
4 1 1
4 2 2
1 2 1
1 3 2
2 4 1
1 3 2

参考资料:

1.图论-桥/割点/双连通分量/缩点/LCA

2. uva 1310 - One-way traffic(连通分量)

3.【图论】求无向连通图的割点

时间: 2024-10-13 16:27:20

算法笔记_149:图论之桥的应用(Java)的相关文章

算法笔记_139:二分图的最大权分配(Java)

目录 1 问题描述 2 解决方案   1 问题描述 何为二分图的最大权匹配问题? 最大权二分匹配问题就是给二分图的每条边一个权值,选择若干不相交的边,得到的总权值最大. 2 解决方案 对于此问题的讲解,引用文末参考资料1: 解决这个问题可以用KM算法.理解KM算法需要首先理解"可行顶标"的概念.可行顶标是指关于二分图两边的每个点的一个值lx[i]或ly[j],保证对于每条边w[i][j]都有lx[i]+ly[j]-w[i][j]>=0.如果所有满足lx[i]+ly[j]==w[i

算法笔记_131:出现次数超过一半的数(Java)

目录 1 问题描述 2 解决方案 2.1 每次删除两个不同的数 2.2 记录两个值   1 问题描述 数组中有一个数出现的次数超过了数组长度的一半,请找出这个数. 2 解决方案 2.1 每次删除两个不同的数 具体代码如下: package com.liuzhen.practice; public class Main { public int getResult(int[] A) { //找到两个不相等的元素,将这两个元素变为0 for(int start = 0, i = 1;i < A.le

算法笔记_198:历届试题 打印十字图(Java)

目录 1 问题描述 2 解决方案   1 问题描述 问题描述 小明为某机构设计了一个十字型的徽标(并非红十字会啊),如下所示: ..$$$$$$$$$$$$$....$...........$..$$$.$$$$$$$$$.$$$$...$.......$...$$.$$$.$$$$$.$$$.$$.$...$...$...$.$$.$.$$$.$.$$$.$.$$.$.$...$...$.$.$$.$.$.$$$$$.$.$.$$.$.$...$...$.$.$$.$.$$$.$.$$$.$.

算法笔记_182:历届试题 核桃的数量(Java)

目录 1 问题描述 2 解决方案   1 问题描述 问题描述 小张是软件项目经理,他带领3个开发组.工期紧,今天都在加班呢.为鼓舞士气,小张打算给每个组发一袋核桃(据传言能补脑).他的要求是: 1. 各组的核桃数量必须相同 2. 各组内必须能平分核桃(当然是不能打碎的) 3. 尽量提供满足1,2条件的最小数量(节约闹革命嘛) 输入格式 输入包含三个正整数a, b, c,表示每个组正在加班的人数,用空格分开(a,b,c<30) 输出格式 输出一个正整数,表示每袋核桃的数量. 样例输入1 2 4 5

算法笔记_189:历届试题 横向打印二叉树(Java)

目录 1 问题描述 2 解决方案   1 问题描述 问题描述 二叉树可以用于排序.其原理很简单:对于一个排序二叉树添加新节点时,先与根节点比较,若小则交给左子树继续处理,否则交给右子树. 当遇到空子树时,则把该节点放入那个位置. 比如,10 8 5 7 12 4 的输入顺序,应该建成二叉树如下图所示,其中.表示空白. ...|-1210-|...|-8-|.......|...|-7.......|-5-|...........|-4 本题目要求:根据已知的数字,建立排序二叉树,并在标准输出中横

算法笔记_181:历届试题 回文数字(Java)

目录 1 问题描述 2 解决方案   1 问题描述 问题描述 观察数字:12321,123321 都有一个共同的特征,无论从左到右读还是从右向左读,都是相同的.这样的数字叫做:回文数字. 本题要求你找到一些5位或6位的十进制数字.满足如下要求: 该数字的各个数位之和等于输入的整数. 输入格式 一个正整数 n (10<n<100), 表示要求满足的数位和. 输出格式 若干行,每行包含一个满足要求的5位或6位整数. 数字按从小到大的顺序排列. 如果没有满足条件的,输出:-1 样例输入 44 样例输

算法笔记_013:汉诺塔问题(Java递归法和非递归法)

目录 1 问题描述 2 解决方案  2.1 递归法 2.2 非递归法 1 问题描述 Simulate the movement of the Towers of Hanoi Puzzle; Bonus is possible for using animation. e.g. if n = 2 ; A→B ; A→C ; B→C; if n = 3; A→C ; A→B ; C→B ; A→C ; B→A ; B→C ; A→C; 翻译:模拟汉诺塔问题的移动规则:获得奖励的移动方法还是有可能的.

算法笔记168:历届试题 矩阵翻硬币(Java)

目录 1 问题描述 2 解决方案   1 问题描述 问题描述 小明先把硬币摆成了一个 n 行 m 列的矩阵. 随后,小明对每一个硬币分别进行一次 Q 操作. 对第x行第y列的硬币进行 Q 操作的定义:将所有第 i*x 行,第 j*y 列的硬币进行翻转. 其中i和j为任意使操作可行的正整数,行号和列号都是从1开始. 当小明对所有硬币都进行了一次 Q 操作后,他发现了一个奇迹--所有硬币均为正面朝上. 小明想知道最开始有多少枚硬币是反面朝上的.于是,他向他的好朋友小M寻求帮助. 聪明的小M告诉小明,

算法笔记_185:历届试题 格子刷油漆(Java)

目录 1 问题描述 2 解决方案   1 问题描述 问题描述 X国的一段古城墙的顶端可以看成 2*N个格子组成的矩形(如下图所示),现需要把这些格子刷上保护漆. 你可以从任意一个格子刷起,刷完一格,可以移动到和它相邻的格子(对角相邻也算数),但不能移动到较远的格子(因为油漆未干不能踩!) 比如:a d b c e f 就是合格的刷漆顺序. c e f d a b 是另一种合适的方案. 当已知 N 时,求总的方案数.当N较大时,结果会迅速增大,请把结果对 1000000007 (十亿零七) 取模.