Diskuze: Teorie grafů: Nalezení kružnice určité délky
V předchozím kvízu, Online test znalostí Java, jsme si ověřili nabyté zkušenosti z kurzu.


Silvinios:26.3.2015 21:57
Uvážíme-li úplný graf, pak kružnici délky n tvoří libovolných n vrcholů v libovolném pořadí. Myslím, že obecně neexistuje lepší řešení než vygenerovat všechny n-tice vrcholů a ověřit, zda tvoří kružnici.
Bez dodatečných předpokladů je těžké hledat nejrychlejší řešení. Víš např. jak velké je n nebo kolik vrcholů a hran graf má?
Ahoj, zkusil jsem to do šířky a potřeboval bych to nějak optimalizovat, co nejvíc zrychlit. Kód hledá kružnice délky 5 - tzv. komplexy, ale mezi vrcholy nesmí být kratší smyčky. Komplexy pak seřazené vypisuje. Nejdůležitější je ale co nejrychlejší kód, aby rychle zpracoval i velmi mnoho vrcholů a jejich hran.
Program jsem okomentoval, aby bylo jasné, o co jde, hledám posloupnost
pěti vrcholů tzv. komplexů, nesmí mít mezi sebou žádné smyčky, tzn,
žádné menší kružnice. Už s tím bojuju asi 3 dny a stále nevím, kde je
problém. Prosím moc o pomoc
package alg;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedList;
import java.util.List;
import java.util.TreeSet;
import java.util.logging.Level;
import java.util.logging.Logger;
;
/**
*
* @author mnaw
* vyuzivam teorie grafu, kazda molekula predstavuje vrchol grafu, dvojice
* molekul jsou hrany.
* http://teorie-grafu.cz/
*
* Co je to graf?
* Graf je dvojice dvou mnozin - mnozin vrcholu a mnozin hran
* vrchol je jakysi bod
* hrana je spojnice mezi body
* kruznice delky n je posloupnost n vrcholu, kruznice musi vest pres hrany
*
*
* Komplexy hledam prohledavanim do hloubky, kde hledam kruznice grafu o
* velikosti 5 vrcholu.
*
* Omezujici podminky - ktere vrcholy neprohledavam:
* 1) vrcholy mensi nez pocatecni vrchol
* 2) vrcholy, ktere uz v kruznici mam
* 3) vrcholy, ktere sousedi s vrcholy v kruznici, krome pripadu, kdy posledni
* vrchol spojuje pocatek a tim uzavre kruh
*
* petice jeste otestuju, zda nejsou duplicitni podminkou, zda posledni prvek
* je vetsi nez druhy
*
*
*
*
*
* na uloze delam cca 60 h
* vetsinu casu zabralo snizeni casu
*/
public class Main {
static int[][] vertices; // tabulka vrcholu, pole vrcholu
// vertices[x] hrany vrcholu x,
// vertices[x][h] nejaka konkretni
// hrana
static final int[] petice = new int[5]; // petice cisel
static int pocatek; // cislo vrcholu, od ktereho
// prohledavam
static int pocetHranSouseda = 0; // poloha nejakeho sousedniho prvku
static final LinkedList<int[]> komplexy = new LinkedList<>(); // seznam komplexu
static final boolean FROM_FILE = true;
static final boolean SHOW_TIME = true;
static final String TEST_FILE = "pub04.in";
static final OutputStream out = new BufferedOutputStream(System.out);
private static int pocetVPetici; // kde v pětici jsem (nebo kolik je v petici
// platnych prvku nebo na jakou pozici zapise)
private static int[] pocetHran; // pocetHran[x] - pocet hran vrcholu x
private static int[] serazenaPetice = new int[5];
public static void main(String[] args) throws IOException {
// TODO code application logic here
long start = System.currentTimeMillis();
BufferedReader sc = null;
if (FROM_FILE) {
try {
sc = new BufferedReader(new InputStreamReader(new FileInputStream(new File("public/" + TEST_FILE))));
} catch (FileNotFoundException ex) {
Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
}
} else {
sc = new BufferedReader(new InputStreamReader(System.in));
}
//nacteni vstupu
String ln = sc.readLine();
int nodes = Integer.parseInt(ln.split(" ")[0]);
int edges = Integer.parseInt(ln.split(" ")[1]);
vertices = new int[nodes][200];
pocetHran = new int[nodes];
//for - nacteni hran a pridani do poli vrcholů (vertices)
for (int i = 0; i < edges; i++) {
String line = sc.readLine();
int refVert1 = Integer.parseInt(line.split(" ")[0]);
int refVert2 = Integer.parseInt(line.split(" ")[1]);
vertices[refVert1][pocetHran[refVert1]] = refVert2;
pocetHran[refVert1]++;
vertices[refVert2][pocetHran[refVert2]] = refVert1;
pocetHran[refVert2]++;
}
sc.close();
// for - kazdy vrchol volim jako pocatek a prohledavam od nej
for (int i = 0; i < vertices.length - 1; i++) {
pocatek = i;
//prohledavam
dfs(pocatek);
pocetVPetici = 0;
// zapisu na vystup nalezene petice
for (int[] petice1 : komplexy) {
for (int cislo = 0; cislo < 5; cislo++) {
out.write((petice1[cislo] + "").getBytes());
if (cislo < 4) {
out.write((" ").getBytes());
}
}
out.write(("\n").getBytes());
}
// vyprazdnim seznam komplexu
komplexy.clear();
out.flush();
}
long end = System.currentTimeMillis();
if (SHOW_TIME) {
System.out.println("Time: " + (end - start) + " ms");
}
out.close();
}
/**
* Prohledavani do hloubky
*
* @param v vrchol ze ktereho prohledavam
*/
public static void dfs(Integer v) {
// vracim se, kdyz uz mam v petici 5 prvku
// vracim se, pokud vrchol je mensi nez pocatek - tim uz jsem prochazel
// vracim se, pokud vrchol ma mene nez 2 hrany - nemuze byt v komplexu
if (pocetVPetici == 5 || v < pocatek || pocetHran[v] < 2) {
return;
}
// pokud se nevracim, pridam si ho do sve petice,
// tim se zvysi i pocet v petici
petice[pocetVPetici] = v;
pocetVPetici++;
// for - prochazim hrany vrcholu
prochazeniSousedu:
for (int hrana = 0; hrana < pocetHran[v]; hrana++) {
// v1 je soused v
int v1 = vertices[v][hrana];
// vrcholy s mensim cislem nez pocatek zahazuji
// pokud maji mene hran nez dve, taky zahazuji
if (v1 < pocatek || pocetHran[v1] < 2) {
continue;
}
// pokud mam 4 cisla a sousedni vrchol je pocatek - pak se kruh uzavira
// treti podminka plni funkci jednosmerky
if (pocetVPetici >= 5 && v1 == pocatek && petice[4] > petice[1]) {
// vytvorim novou petici, prekopiruji do ni prvky ze stare petice
serazenaPetice = new int[5];
// nakopiruju si pole, do noveho pole, ktere chci pridat do seznamu
System.arraycopy(petice, 0, serazenaPetice, 0, 5);
// seradim novou petici
Arrays.sort(serazenaPetice);
// zapisu do seznamu komplexu
int ksize = komplexy.size();
if (ksize == 0) {
komplexy.add(serazenaPetice);
}
for (int i = 0; i < ksize; i++) {
if (compare(serazenaPetice, komplexy.get(ksize - 1 - i))) {
komplexy.add(ksize - i, serazenaPetice);
break;
} else {
komplexy.add(ksize - i - 1, serazenaPetice);
break;
}
}
// chci se vratit, proto "odstranim" prvek z petice
pocetVPetici--;
return;
}
for (int i = 0; i < pocetVPetici; i++) {
if (petice[i] == (v1)) {
continue prochazeniSousedu;
}
}
pocetHranSouseda = pocetHran[v1];
// nez pujdu do hloubky:
// overuji, zda cislo sousedniho vrcholu je vetsi nez cislo pocatecniho
// overuji, zda souseda nemam v petici
// overuji, zda soused netvori smycky s petici (aby kazdy prvek petice sousedil prave dvakrat)
if (v1 >= pocatek && !contains(petice, v1) && pocetSpolecnychPrvku(petice, vertices[v1]) < 2) {
dfs(v1);
}
}
// prohledany vsechny hrany, vracim se
if (pocetVPetici > 0) {
pocetVPetici = pocetVPetici - 1;
}
}
/**
* pocet spolecnych prvku v poli, pokud mam 4 prvky v petici, tak ocekavam smycku s
* pocatkem (odecitam 1)
*
* @param l1
* @param l2
* @return
*/
public static int pocetSpolecnychPrvku(int[] l1, int[] l2) {
int ret = 0;
if (pocetVPetici >= 4) {
ret -= 1;
}
for (int i = 0; i < pocetVPetici; i++) {
for (int j = 0; j < pocetHranSouseda; j++) {
if ((l2[j] == l1[i])) {
ret++;
}
if (ret >= 2) {
return ret;
}
}
}
return ret;
}
/**
* zda obsahuje pole array hodnotu value
*
* @param array pole, ktere otestuji
* @param value hodnota, kterou hledam v poli
* @return
*/
public static boolean contains(int[] array, Integer value) {
for (int i = 0; i < pocetVPetici; i++) {
if (array[i] == (value)) {
return true;
}
}
return false;
}
/**
* porovnani dvou poli, porovnava se podle prvniho prvku, pokud jsou prvni
* prvky stejne, porovnava se s druhym, atd....
*
* @param a1
* @param a2
* @return true, pokud je prvni pole vetsi, jinak false
*/
public static boolean compare(int[] a1, int[] a2) {
for (int i = 1; i < 5; i++) {
if (a1[i] > a2[i]) {
return true;
}
if (a1[i] < a2[i]) {
return false;
}
}
return false;
}
}
Lukáš Hruda:29.3.2015 23:00
Šel bych na to spíš přes průchod do hloubky. Podívej se po nějaký algoritmech pro hledání silně souvislých komponent orientovaných grafů, většina z nich funguje právě na principu hledání kružnic. Možná bys některý z nich mohl modifikovat tak, aby hledal pouze nejmenší kružnice. Zkusil bych třeba Cheriyan-Mehlhornův algoritmus. Každopádně si myslím, že je to problém, který by měl jít vyřešit jedním průchodem.
Zobrazeno 5 zpráv z 5.