Bonjour,
J'ai actuellement un projet en C ou je dois réaliser un arbre couvrant minimal pour des points possédant une coordonnée x et y.
N'ayant jamais vu rien de proche en cours, j'ai essayé de trouver par moi même un algorithme. Celui-ci est très proche de la solution car dans la plupart des cas j'atteint le bon résultat. Néanmoins, il semblerait que dans de (rares) cas je n'obtienne pas la solution attendue. N'ayant pas accès aux Input des tests, je ne peux pas savoir ce qui pose problème. Donc il faut que je reprenne mon algo & que j'essaye de voir ce qui plante.
Voici l'algo auquel j'avais pensé.
- 1/ On calcule la matrice d'adjacence entre les sommets
- 2/ On trie cette matrice par poids (en gardant a disposition les infos des arbres à relier)
- 3/ En sachant que si il y a n nœuds dans l'arbre, il y à n-1 liaisons, on cherche les n-1 liaisons de poids le plus faible qui ne forment pas de cycle
- 4/ Et on retourne la somme des poids des ces n-1 liaisons.
Déjà, est ce que cet algo est correct?
Si oui, voici son implémentation, il faudra que je trouve l'erreur. Je suis conscient que mon algorithme n'est pas tout à fait optimisé. Par exemple il n'était pas nécessaire d'utiliser une matrice d'adjacence complète puisque les liaisons sont pas orientés. Cela dit, la complexité de mon implémentation semble assez faible donc ça doit rester correct.
Fonction principale
Code c : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42 int main(int argc, char** argv) { int i,j; int nbIles; scanf("%d", &nbIles); vector2 iles[nbIles]; cost costMatrix[nbIles*nbIles]; ///Implémentée sur un tableau simple for(i=0;i<nbIles ;++i) /// On saisit les coordonnées des îles scanf("%d %d",&(iles[i].x),&(iles[i].y)); for(i=0;i<nbIles;++i) /// On marque les coûts d'accès entre les sommets. for(j=0;j<nbIles;++j) if(i!=j) { costMatrix[i * nbIles + j].cost = sqrt( (iles[i].x-iles[j].x)*(iles[i].x-iles[j].x) +(iles[i].y-iles[j].y)*(iles[i].y-iles[j].y)); ///Distance en norme 2 classique. costMatrix[i * nbIles + j].from = i; costMatrix[i * nbIles + j].to = j; } else costMatrix[i * nbIles + j].cost = INT_MAX; /// On ne veux pas que ces valeurs apparaissent (INT_MAX simule l'infini). Sort(costMatrix,0,nbIles*nbIles-1);/// Puis on trie les coûts int nbArcs = 0; i=0; list* FinalNodes = calloc(nbIles,sizeof(list)); double Weight =0; while (nbArcs < nbIles - 1) ///Ca c'est un algo bien déguelasse comme on les aime ! { if(!FindCycle(FinalNodes,nbIles,costMatrix[i].from,costMatrix[i].to)) { AddItem(&(FinalNodes[costMatrix[i].from]),costMatrix[i].to);///On ajoute les noeuds à la matrice d'adjacence AddItem(&(FinalNodes[costMatrix[i].to]),costMatrix[i].from);///On ajoute les noeuds à la matrice d'adjacence ++nbArcs; /// 1 arc de plus Weight += costMatrix[i].cost; /// On met à jour le poids. } i+=2; /// Deux valeurs sont égales à chaque fois car on a pris une matrice d'adjacence compléte. On avance donc de 2. } printf("%d", (int)Weight); return 0; }
Structure utilisées
Code c : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29 struct cell { int to; struct cell* next; }; typedef struct cell cell; typedef cell* list; void AddItem(list* l, int to) { cell* c = malloc(sizeof(cell)); c->next = NULL; c->to = to; list tmp = *l; *l = c; (*l)->next = tmp; } struct vector2 { int x,y; }; typedef struct vector2 vector2; struct cost { int from,to; double cost; }; typedef struct cost cost;
Algorithme de tri (ici quicksort), je crois qu'il est correct
Code c : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56 void swap ( cost* a, cost* b ) { int t = a->from; a->from = b->from; b->from = t; t = a->to; a->to = b->to; b->to = t; double td = a->cost; a->cost = b->cost; b->cost = td; } int partition (cost costs[], int l, int h) { int x = costs[h].cost; int i = (l - 1); int j; for (j = l; j <= h- 1; j++) { if (costs[j].cost <= x) { i++; swap (&costs[i], &costs[j]); } } swap (&costs[i + 1], &costs[h]); return (i + 1); } void Sort(cost costs[], int l, int h) { int stack[ h - l + 1 ]; int top = -1; stack[ ++top ] = l; stack[ ++top ] = h; while ( top >= 0 ) { h = stack[ top-- ]; l = stack[ top-- ]; int p = partition( costs, l, h ); if ( p-1 > l ) { stack[ ++top ] = l; stack[ ++top ] = p - 1; } if ( p+1 < h ) { stack[ ++top ] = p + 1; stack[ ++top ] = h; } } }
Et fonction de recherche de cycle
Code c : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26 bool FindCycle(list* finalNodes, int nbIles, int from, int to) { bool* EstParcouru = calloc(nbIles,sizeof(bool)); list stack = NULL; AddItem(&stack,from); while (stack != NULL) { list tmp = stack; int cell = stack->to; stack = stack->next; free(tmp); list nexts = finalNodes[cell]; while(nexts) { if(nexts->to == to) ///On a atteint le début du cycle return true; else if(!EstParcouru[nexts->to]) { AddItem(&stack,nexts->to); EstParcouru[nexts->to] = true; } nexts = nexts->next; } } return false; }
Outre les problèmes d'optimisation (qui sont un autre problème pour l'instant) voyez vous des problèmes plus importants pouvant conduire à des résultats faux?
Je vous remercie pour votre aide,
Maxime.
Partager