Hledání nepostradatelných mostů je častou úlohou teorie grafů, jejímž cílem je nalézt takové hrany, po jejichž odebrání by se daný neorientovaný graf rozpadl na více částí. Pokud tuto úlohu přeformulujeme, tak hledáme takové hrany, které nejsou součástí žádného cyklu (ke všem uzlům uvnitř cyklu vede přinejmenším jedna další alternativní cesta).
Řešení
Řešením této úlohy je prohledávání do hloubky rozšířené o některé aspekty Tarjanova algoritmu. Pokud při prohledávání grafu narazíme na zpětnou hranu vedoucí do uzlu U, pak všechny stromové hrany průchodu až do návratu do uzlu U jsou uvnitř cyklu. Pokud objevíme příčnou hranu, pak nemusíme činit nic, protože se jedná o již zpracovaný případ zpětné hrany (objevený z opačné strany).
Za předpokladu, že uzel leží ve více cyklech, musíme rozhodnout, který cyklus je nejdelší (ve smyslu návratu z rekurze). Což je zřejmě ten, jehož cílovým uzlem je uzel, který byl objeven nejdříve (je ve stromu průchodu nejvýše).
Složitost tohoto řešení je totožná se složitostí prohledávání do hloubky, čili .
Příklad
Nalezněte nepostradatelné mosty. Vstupní data budou programu předána na standardním vstupu. Výstup bude realizován pomocí standardního výstupu.
Vstup
Prvním řádkem vstupu je počet uzlů grafu, na dalších řádcích se vyskytují dvojice oddělené mezerou reprezentující hrany (počáteční uzel, koncový uzel). Vstup je ukončen dvojicí „0 0“.
Vzor:
0 3
1 3
2 3
4 5
4 6
4 7
3 4
0 1
1 2
5 6
6 7
0 0
Výstup
Výstupem algoritmu jsou dvojice uzlů oddělené mezerou reprezentující nepostradatelnou hranu grafu (nepostradatelný most), na pořadí uzlů nezáleží. Výstup je ukončen dvojicí „0 0“.
Vzor:
0 0
Kód
001.
/**
002.
* Resi problem nepostradatelneho mostu
003.
* @author Pavel Micka
004.
*/
005.
public
class
Main {
006.
/**
007.
* Vystupni buffer
008.
*/
009.
private
static
StringBuilder builder =
new
StringBuilder(
4096
);
010.
/**
011.
* Citac
012.
*/
013.
private
static
int
counter =
0
;
014.
015.
/**
016.
* @param args the command line arguments
017.
*/
018.
public
static
void
main(String[] args)
throws
IOException {
019.
BufferedReader r =
new
BufferedReader(
new
InputStreamReader(System.in),
2048
);
020.
021.
int
nodeCount = Integer.valueOf(r.readLine());
022.
Node[] nodes =
new
Node[nodeCount];
023.
024.
if
(nodeCount ==
0
){
025.
System.out.println(
"0 0"
);
026.
return
;
027.
}
//zadny uzel - zadny problem
028.
029.
for
(
int
i =
0
; i < nodes.length; i++) {
030.
nodes[i] =
new
Node(i);
031.
}
032.
033.
readInput(r, nodes);
034.
035.
nodes[
0
].setState(Node.OPEN);
036.
solve(nodes[
0
],
null
);
037.
nodes[
0
].setState(Node.CLOSED);
038.
039.
builder.append(
"0 0"
);
//uzavri vstup
040.
041.
System.out.println(builder.toString());
//vystup
042.
}
043.
044.
/**
045.
* Precte vstupni data
046.
* @param r vstupni stream
047.
* @param nodes pole uzlu
048.
* @throws IOException v pripade, ze nelze ze streamu cist
049.
* @throws NumberFormatException v pripade nevalidnich dat (spane reprezentace cisel) ve streamu
050.
*/
051.
private
static
void
readInput(BufferedReader r, Node[] nodes)
throws
IOException, NumberFormatException {
052.
String line =
null
;
053.
while
((line = r.readLine()) !=
null
) {
054.
StringTokenizer tokenizer =
new
StringTokenizer(line,
" "
);
055.
int
parent = Integer.valueOf(tokenizer.nextToken());
056.
int
child = Integer.valueOf(tokenizer.nextToken());
057.
nodes[child].getNeighbours().add(nodes[parent]);
058.
nodes[parent].getNeighbours().add(nodes[child]);
059.
}
060.
}
061.
062.
/**
063.
* Resi pruchodem do hloubky problem nepostradelnych mostu
064.
* @param node zpracovavany uzel
065.
* @param parent rodic tohoto uzlu
066.
* @return cislo nejvyse dosazitelneho uzlu cyklem, Integer.MAX_VALUE v pripade,
067.
* ze takovy uzel neexistuje
068.
*/
069.
private
static
int
solve(Node node, Node parent) {
070.
node.setDiscovered(counter++);
071.
int
cycleTo = Integer.MAX_VALUE;
//cyklus neexistuje
072.
for
(Node n : node.getNeighbours()) {
//pro vsechny sousedy
073.
if
(!n.equals(parent)) {
//kteri nejsou rodicem uzlu
074.
if
(n.getState() == Node.FRESH) {
//a jsou cerstvi
075.
n.setState(Node.OPEN);
//otevri je
076.
int
childCycleTo = solve(n, node);
//zjisti nejvyssi uzel, na lze ktery dosahnout
077.
if
(childCycleTo < cycleTo && childCycleTo != node.getDiscovered()) {
//pokud to neni tento uzel a nedosahneme jiz na vyssi
078.
cycleTo = childCycleTo;
//pak jsme objevili nejvyssi dosazitelny uzel
079.
}
else
if
(childCycleTo == Integer.MAX_VALUE) {
//pokud cyklus z ditete neexistuje
080.
builder.append(node.getId() +
" "
+ n.getId() +
"\\n"
);
//pak je tento most nepostradatelny
081.
}
082.
}
else
if
(n.getState() == Node.OPEN) {
//pokud je uzel otevreny
083.
cycleTo = n.getDiscovered();
//objevili jsme zpetnou hranu (cyklus)
084.
}
085.
}
086.
}
087.
node.setState(Node.CLOSED);
//uzavri uzel
088.
return
cycleTo;
//vrat nejvyssi dosazitelny uzel
089.
}
090.
}
091.
092.
/**
093.
* Uzel grafu
094.
* @author Pavel Micka
095.
*/
096.
class
Node {
097.
private
int
discovered;
//identifikator uzlu
098.
private
int
id;
//identifikator uzlu
099.
public
static
final
int
FRESH =
0
;
//cerstvy uzel
100.
public
static
final
int
OPEN =
1
;
//otevreny uzel
101.
public
static
final
int
CLOSED =
2
;
//uzavreny uzel
102.
private
List<Node> neighbours;
//sousede uzlu
103.
private
int
state;
//sousede uzlu
104.
105.
public
Node(
int
id) {
106.
neighbours =
new
ArrayList<Node>();
107.
this
.id = id;
108.
state = FRESH;
109.
}
110.
111.
/**
112.
* @return the discovered
113.
*/
114.
public
int
getDiscovered() {
115.
return
discovered;
116.
}
117.
118.
/**
119.
* @param discovered the discovered to set
120.
*/
121.
public
void
setDiscovered(
int
discovered) {
122.
this
.discovered = discovered;
123.
}
124.
125.
/**
126.
* @return the id
127.
*/
128.
public
int
getId() {
129.
return
id;
130.
}
131.
132.
/**
133.
* @param id the id to set
134.
*/
135.
public
void
setId(
int
id) {
136.
this
.id = id;
137.
}
138.
139.
/**
140.
* @return the neighbours
141.
*/
142.
public
List<Node> getNeighbours() {
143.
return
neighbours;
144.
}
145.
146.
/**
147.
* @param neighbours the neighbours to set
148.
*/
149.
public
void
setNeighbours(List<Node> neighbours) {
150.
this
.neighbours = neighbours;
151.
}
152.
153.
/**
154.
* @return the state
155.
*/
156.
public
int
getState() {
157.
return
state;
158.
}
159.
160.
/**
161.
* @param state the state to set
162.
*/
163.
public
void
setState(
int
state) {
164.
this
.state = state;
165.
}
166.
}