c++/graf wazony/ reprezentacja listowa

0

Witam mam do napisania program testujący czas pracy algorytmów Prima i Dijkstry. Są to algorytmy wyznaczające MDR(minimalne drzewo rozpinające ang. MST). Jednak problem pojawił się już na początku ze zrobieniem samego grafu z wagami. Poniżej wrzucę kod tego co zrobiłem do tej pory (czyli utworzyłem graf, losuje wagi ale nie potrafię ich przypisać do odpowiednich krawędzi) i może ktoś podpowie jak przypisać tam wagi odpowiednim przejściom bo próbuję na różne sposoby i mi nie wychodzi:


#include<iostream>
#include<vector>
#include<time.h>
#include <cstdlib>
#include <ctime>
#include <algorithm>
using namespace std;

int main()
{
	srand(time(NULL));
	int V, E;     // V - liczba wierzcholkow, E - liczba krawedzi
	V=10;       //początkowo później będzie więcej (jak będę pewny ,że działa dobrze)
	E=2*V;
	cout << "Liczba wierzcholkow: "<<V<<endl;
	cout << "Liczba krawedzi: "<<E<<endl;
	vector<int> *ZA = new vector<int>[V+1]; 

	for(int i=1; i<=E; i++)      
	{
		int a,b,waga;
		a=rand()%V+1;
		b=rand()%V+1;
		waga=rand()%V/2+1;

		if(a!=b)
		{
			cout << a<<":"<<b<<"->"<<waga<<endl;
			ZA[a].push_back(b);
			ZA[b].push_back(a);  // tutaj nie wiem jak dalej tą wagę przypisać do danego połączenia
		}	
	}
	for(int i=1; i<=V; i++)     // wypisujemy graf
	{
		cout << endl << "Sasiedzi wierzcholka " << i << ": ";

		//sortujemy i usuwamy powtarzajace sie polaczenia
		sort(ZA[i].begin(),ZA[i].end());
		vector<int>::iterator itt=unique(ZA[i].begin(),ZA[i].end());
		ZA[i].erase(itt,ZA[i].end());

		for(vector<int>::iterator it = ZA[i].begin(); it != ZA[i].end(); ++it) 
		{
			cout << *it <<", ";
		}
		
	}
	cout<<endl;
	delete []ZA;     // zwalniamy pamiec
	system("pause");
}
 
1

algorytmów Prima i Dijkstry. Są to algorytmy wyznaczające MDR(minimalne drzewo rozpinające ang. MST)

O RLY? A od kiedy? Może ci się Dijkstra z Kruskalem pomylił? Bo Dijkstra tak jak Bellman-Ford wyznacza najkrótsze ścieżki w grafie z jednym źródlem...

A kod to tragedia. Lekcje na dziś:

  • klasy albo przynajmniej struktury
  • tablice (i vectory) indeksujemy od 0 do n-1 a nie od 1 do n
  • istnieje coś takiego jak <list>...
  • Nazywaj zmienne jak człowiek! Nazwa ZA to gówniana nazwa...
1
Shalom napisał(a)

O RLY? A od kiedy? Może ci się Dijkstra z Kruskalem pomylił? Bo Dijkstra tak jak Bellman-Ford wyznacza najkrótsze ścieżki w grafie z jednym źródlem...

Tak masz racje późna pora i już trochę nad tym siedzę ... .

Shalom napisał(a)

A kod to tragedia. Lekcje na dziś:

  • klasy albo przynajmniej struktury
  • tablice (i vectory) indeksujemy od 0 do n-1 a nie od 1 do n
  • istnieje coś takiego jak <list>...
  • Nazywaj zmienne jak człowiek! Nazwa ZA to gówniana nazwa...

Trochę drastycznie , ale masz racje ;d . Co do klasy i struktur na razie nie chciałem komplikować sobie życia i chciałem stworzyć jak najprościej graf wagowy ,a później planowałem napisać to porządnej lecz żeby to zrobić muszę wiedzieć jak przypisać te wagi do poszczególnych połączeń. A niestety twój post niezbyt mi pomaga w tym.

1

Nie planuj porządnego pisania "na później" tylko od razu pisz to jak człowiek. Zrób sobie klasę odpowiedzialną za krawędzie. Zrób klasę odpowiedzialną za węzły. Wtedy nie będzie problemów z serii "gdzie wpisać wagę?".
Każdy węzeł niech przechowuje sobie listę Krawędzi. Każda Krawędź niech ma informacje o węźle źródłowym, docelowym i wagę.
Oprócz tego zrób klasę Graf która będzie przechowywać vector węzłów.

Takie rozbicie tego na klasy wcale niczego nie utrudnia! Wręcz przeciwnie! Taka reprezentacja sprawia że to wszystko robi się przejrzyste i banalnie łatwe! Bo każda z tych klas osobno jest sama w sobie prosta. Problemy rodzą się właśnie kiedy próbujesz wszystko upchnąć w jednym miejscu.

0

Ok zrobiłem chyba tak jak pisałeś, ale dalej nie do końca widzę jak przypisać tą wagę oto poprawiony kod:

#include <iostream>
#include <list>
#include <vector>
#include <algorithm>
#include <time.h>

using namespace std;

class krawedz
{
public:
	int wezel_zrodlowy;
	int wezel_docelowy;
	int waga;
};
class wezel
{
public:
	list<int> krawedz;
};
class graf
{
public:
	vector<int> *wezel;
};
void lista_sasiedztwa()
{
	int ilosc_wierzcholkow=10;
	int ilosc_krawedzi=ilosc_wierzcholkow*2;
	krawedz krawedzie;
	wezel wezly;
	graf grafy;
	grafy.wezel=new vector<int>[ilosc_wierzcholkow+1];
	cout<<"Liczba wierzcholkow to: "<<ilosc_wierzcholkow<<endl<<"Liczba krawedzi to: "<<ilosc_krawedzi<<endl;
	//wprowadzenie krawedzi i ich wag
	for(int i=0;i<ilosc_krawedzi;i++)
	{
		krawedzie.waga=rand()%ilosc_wierzcholkow/2+1;
		krawedzie.wezel_zrodlowy=rand()%ilosc_wierzcholkow+1;
		krawedzie.wezel_docelowy=rand()%ilosc_wierzcholkow+1;
		if(krawedzie.wezel_zrodlowy!=krawedzie.wezel_docelowy)
		{
			cout <<krawedzie.wezel_zrodlowy<<":"<<krawedzie.wezel_docelowy<<"->"<<krawedzie.waga<<endl;
			grafy.wezel[krawedzie.wezel_zrodlowy].push_back(krawedzie.wezel_docelowy);
			grafy.wezel[krawedzie.wezel_docelowy].push_back(krawedzie.wezel_zrodlowy);
		}
	}
	for(int i=1; i<=ilosc_wierzcholkow; i++)     // wypisujemy graf
	{
		cout << endl << "Sasiedzi wierzcholka " << i << ": ";
		//sortujemy i usuwamy powtarzajace sie polaczenia
		sort(grafy.wezel[i].begin(),grafy.wezel[i].end());
		vector<int>::iterator itt=unique(grafy.wezel[i].begin(),grafy.wezel[i].end());
		grafy.wezel[i].erase(itt,grafy.wezel[i].end());

		for(vector<int>::iterator it = grafy.wezel[i].begin(); it != grafy.wezel[i].end(); ++it) 
		{
			cout << *it <<", ";
		}
	}
	delete []grafy.wezel; //zwolnienie pamieci
}
void main()
{
	srand(time(NULL));
	lista_sasiedztwa();
	cout<<endl;
	system("pause");
} 
0

Nie. To co zrobiłeś nie ma nic wspólnego z tym o czym pisałem. Krawędź powinna przechowywac wskaźniki do węzłów. Węzeł powinien mieć listę wskaźników do krawędzi. Graf powinien mieć vector wskaźników do węzłów.

0
Shalom napisał(a)

Nie. To co zrobiłeś nie ma nic wspólnego z tym o czym pisałem. Krawędź powinna przechowywac wskaźniki do węzłów. Węzeł powinien mieć listę wskaźników do krawędzi. Graf powinien mieć vector wskaźników do węzłów.

No to nie mam pojęcia za bardzo już , wcześniej pisałeś:

Shalom napisał(a)

Każdy węzeł niech przechowuje sobie listę Krawędzi. Każda Krawędź niech ma informacje o węźle źródłowym, docelowym i wagę.
Oprócz tego zrób klasę Graf która będzie przechowywać vector węzłów.

wiec wydawało mi się ze o to chodzi. Jeśli możesz to może pokaż jedna klasę jak ma wyglądać a ja spróbuje wtedy zrobić resztę bo na razie nie bardzo wiem jak to zapisać.

0

Idziesz w dobrym kierunku, ale w ten sposób niczego nie rozwiązujesz ani niczego właściwie nie zmieniasz - faktycznie, takie podejście komplikuje sprawę.

Poprawnie 'obiektowo' by to wyglądało tak:

// tu by było fajnie template wszędzie zrobić,
// ale szkoda komplikować przykład...

struct edge // struct == class z wszystkimi polami publicznie
{
        node* src;
        node* dst;
        int value;
};

struct node
{
        list<edge*> edges;
};

struct graph
{
        vector<edge> edges; // graf jest właścicielem krawedzi
        vector<node> nodes; // i wierzchołków
};

Dodatkowo operacje powinny być zorganizowane jako metody publiczne a pola raczej pozostać prywatne.

Spróbowałbym pomóc z głównym problemem, tzn. funkcją lista_sąsiedztwa, ale tam trochę masakra jest więc się boję zagłębiać :>

Fakt jest faktem że algorytmicy zazwyczaj piszą bardzo brzydki kod (z dziesiątkami jednoliterowych zmiennych i, j, k, l, m, p, q, v etc)...

0

Witam udało mi się już pozbyć tamtego problemu teraz tylko mam problem z tym jak pozbyć się powtórzeń że np z skad=5 dokad=2 idzie z waga=1 a gdzieś tam później idzie z waga=3, i tak nie może być posortowałem to i użyłem funkcji unique żeby usunąć duplikaty ale ona chyba w tym przypadku jak ja zapisałem usuwa tylko duplikaty które są identyczne na tych trze pozycjach czyli skad,dokad,waga , natomiast usuwać musi mi też jak skąd dokąd są takie same i nie wiem jak to zrobić może coś pomożecie. Oto kod:

 
#include <iostream>
#include <vector>
#include <algorithm>
#include <time.h>

using namespace std;

typedef double waga;
typedef pair<double,waga> krawedz;
typedef vector<krawedz> wezel;
typedef vector<wezel> graf;

void generuj_liste_sasiedztwa()
{
	srand(time(NULL));
	int n,m; //n-liczba wierzcholkow, m-liczba krawedzi
	n=10;
	m=2*n;
	int losuj=n-1;
	graf g(n,wezel());

	for(int j=0;j<2;j++)
	{
		cout<<"Generuje liste sasiedztwa dla "<<n<<" wierzcholkow i "<<m<<" krawedzi"<<endl;
		
			for(int i=0;i<m;i++)
			{	
				int a,b; //a-przejsce skad , b-dokad ->połączenie 2 wierzcholkow
				waga w;
				a=rand()%losuj+1;
				b=rand()%losuj+1;
				w=rand()%losuj/2+1;	
				
				cout<<a<<":"<<b<<"->"<<w<<endl;
				g[a].push_back(krawedz(b,w));
				
			}
		m+=m;

				for(int k=1; k<n; k++)     // wypisujemy graf
				{
                                        //sortujemy i usuwamy powtarzajace sie polaczenia
					cout << endl << "Sasiedzi wierzcholka " << k << ": ";
					sort(g[k].begin(),g[k].end());
					g[k].erase (unique(g[k].begin(), g[k].end()),  g[k].end());
					
					for( vector <pair <double,double> >::iterator dx = g[k].begin(); dx != g[k].end(); dx++ )
					{
						//cout <<  (*dx).first<<":"<<(*dx).second<<",";
						cout << dx->first << "->" << dx->second << " ," ;
					}
					cout<<endl;
		
				}
	}
	
}
void main()
{
	cout<<"....::::PROGRAM START::::...."<<endl;
	generuj_liste_sasiedztwa();
	cout<<"....::::PROGRAM KONIEC::::...."<<endl;
	system("pause");
}
1

Zdefiniuj

int KrawedzSorter(const krawedz &a, const krawedz &b)
{
	return b.first - a.first;
}

i zamiast

sort(g[k].begin(),g[k].end());
g[k].erase (unique(g[k].begin(), g[k].end()),  g[k].end());

daj

sort(g[k].begin(),g[k].end(), KrawedzSorter);
g[k].erase (unique(g[k].begin(), g[k].end(), KrawedzSorter),  g[k].end());
0
adf88 napisał(a)

Zdefiniuj

int KrawedzSorter(const krawedz &a, const krawedz &b)
{
	return b.first - a.first;
}

i zamiast

sort(g[k].begin(),g[k].end());
g[k].erase (unique(g[k].begin(), g[k].end()),  g[k].end());

daj

sort(g[k].begin(),g[k].end(), KrawedzSorter);
g[k].erase (unique(g[k].begin(), g[k].end(), KrawedzSorter),  g[k].end());

przy kompilacji niestety się sypie

0

Po pierwsze ten KrawedzSorter jest niepoprawny, to nie C ani Fortran. Potrzebujesz dwóch różnych funkcji do sorta i unique, dla sorta zachowująca się jak <, dla unique jak ==.

0

Po przetestowaniu na algorytmie Prima działa mi to dobrze nawet na multi grafie także można powiedzieć że nie potrzebuje tego krawedzSortera. Ale mam kolejny problem. Przy robieniu algorytmu Kruskala. Nie wiem za bardzo jak porównywać ze sobą tam odpowiednie wagi. Bo cmp które używam do Prima nie działa mi przy Kruskalu bo sypie błędem. Stworzyłem nowe comp do porówynywania ,ale sypie tym samym błędem. Także proszę o jakąś podpowiedź/pomoc.
I też od razu nie wiem jak odnieść się z main'a do Kruskala. Bo kruskal na wejściu w mainie musi dostać 3 zmienne (które to są w liscie_sasiedztwa czyli a,b,c) ale nie wiem wlasnie tez jak do tego sie odwolac. Oto caly kod:

 
#include<cstdio>
#include<vector>
#include<set>
#include<time.h>
#include<iostream>
#include<algorithm>
using namespace std;
int n,m,rodzic[10000],ranga[10000]; //n-l.wierzcholkow, m-liczba krawedzi
vector< vector< pair<int,int> > > graf;
vector<bool> vis;
vector<int> waga, pi;


void lista_sasiedztwa()
{
	int a,b,c;
	
	graf.resize(n);
	for (int i=0; i<m; i++)
	{
		a=rand()%n+1;
		b=rand()%n+1;
		c=rand()%n/2+1;
		a--; b--;
		cout<<a<<"--"<<b<<":"<<c<<endl;
		graf[a].push_back( make_pair(b,c) );
		graf[b].push_back( make_pair(a,c) );
	}
	cout<<endl;
	for(int k=1; k<n; k++)     // wypisujemy graf
	{
		cout << "Sasiedzi wierzcholka " << k << ": ";
		sort(graf[k].begin(),graf[k].end());
		graf[k].erase (unique(graf[k].begin(), graf[k].end()),  graf[k].end());
		for( vector <pair <int,int> >::iterator dx = graf[k].begin(); dx != graf[k].end(); dx++ )
		{
			cout << dx->first << "->" << dx->second << " ," ;
		}
		cout<<endl;
		
	}
}
struct cmp
{
	// czy a jest mniejsze od b
	bool operator() (const int &a, const int &b)
	{
		if (waga[a] < waga[b]) return true;
		if (waga[a] > waga[b]) return false;
		return a<b;
	}
};
struct comp
{
	bool operator() (const int &w1, const int &w2)
	{
		sort(waga.begin(),waga.end());
		for(int i=0;i<waga.size();i++)
		{
			if (waga[i] < waga[i+1]) 
				return true;
			if (waga[i] > waga[i+1]) 
				return false;
		
		return waga[i]<waga[i+1];
		}
	}
};
set<int, cmp> kopiec;

void algorytm_prima(int s)
{
	cout<<"--ALGORYTM PRIMA--"<<endl;
	int v, u, c;
	waga.clear(); 
	waga.resize(n, 10000000); 
	vis.clear(); 
	vis.resize(n,false);
	pi.resize(n);
 
	waga[s] = 0; 
	pi[s]=s; 
	kopiec.clear(); 
	for (int i=0; i<n; i++) 
		kopiec.insert(i);
	while( !kopiec.empty() )
	{
		u = *(kopiec.begin()); 
		kopiec.erase(kopiec.begin());
		vis[u]=true;
		// a dodał go wierzchołek pi[u]
		for (int i=0; i<graf[u].size(); i++)
		{
			v = graf[u][i].first;
			if (!vis[v])
			{
				c = graf[u][i].second;
				if (c < waga[v]) 
				{
					
					kopiec.erase(kopiec.find(v));
					waga[v] = c; // 
					kopiec.insert(v);
					pi[v] = u;
				}
			}
		}
	}
	int suma = 0; 
	for (int i=1; i<n; i++) 
		suma += waga[i]; 
	cout<<endl<<"Koszt minimalnego drzewa spinajacego wynosi : "<< suma<<endl;
	cout<<"Drzewo zlozone jest z nastepujacych krawedzi:"<<endl;
	for (int i=1; i<n; i++) 
		
		cout<< i <<"--"<<pi[i]<<":"<<waga[i]<<endl;
}

int znajdz(int x)
{
	if(x!=rodzic[x])
	{
		rodzic[x]=znajdz(rodzic[x]);
	}
	return rodzic[x];
}

void polacz(int x,int y)
{

	int px=znajdz(x),py=znajdz(y);
	if(ranga[px]>ranga[py])
	{
		rodzic[py]=px;
	}
	else 
	{
		rodzic[px]=py;
	}
	if(ranga[px]==ranga[py])
	{
		++ranga[py];
	}
}
void algorytm_kruskala(int v1,int v2,int w1)
{
	int total=0;
	cout<<"--ALGORYTM KRUSKALA--"<<endl;
	for(int i=1;i<=n;i++)
	{
		rodzic[i]=i;
		ranga[i]=0;
	}
	sort(graf.begin(),graf.end(),comp());
	for(int i=0;i<m;i++)
	{
		int s1=znajdz(v1);
		int s2=znajdz(v2);
		if(s1!=s2)
		{
			polacz(s1,s2);
			total+=w1;
		}
	}
	cout<<"Koszt minimalnego drzewa spinajacego wynosi"<<total<<endl;
}
void main()
{
	cout<<"....::::PROGRAM START::::...."<<endl;
	srand(time(NULL));
	n=5;
	m=2*n;
	lista_sasiedztwa();
	algorytm_prima(0); // zaczynamy algorytm w wierzchołku nr 0
	algorytm_kruskala();
	cout<<endl<<"....::::PROGRAM KONIEC::::...."<<endl;
	system("pause");
}

0

ktoś ma jakiś pomysł jak zrobić tutaj Kruskala???

1 użytkowników online, w tym zalogowanych: 0, gości: 1