// Zkompilování: gcc -std=gnu11 -lm 28-5-2.c // Ukázkový vstup (příklad ze zadání): // 6 // 0 0 // 7 0 // 6 2 // 4 3 // 3 3 // 1 2 #include #include #include #include #define MAXN 100 // D[a][b][0]... délka nejkratší cesty pokrývající úsek [a,b] a končící v $a$. // D[a][b][1]... délka nejkratší cesty pokrývající úsek [a,b] a končící v $b$. double D[MAXN][MAXN][2]; // = 0, pokud poslední hrana cesty je strana, 1, pokud úhlopříčka char P[MAXN][MAXN][2]; int N; struct bod { double x, y; int index; // původní index před setříděním bodů }; struct bod body[MAXN]; int obal[MAXN]; // Vrátí i % N s korektním ošetřením záporných čísel (e.g. modN(-1) == N-1) int modN(int i) { int r = i % N; if (r < 0) r += N; return r; } // Vzdálenost mezi dvěma body určenými indexem na obvodu. double dist(int i, int j) { i = modN(i); j = modN(j); return sqrt(pow(body[obal[i]].x - body[obal[j]].x, 2) + pow(body[obal[i]].y - body[obal[j]].y, 2)); } // lexikografické porovnání bodů int porovnej_body(const void *va, const void *vb) { const struct bod *A = va, *B = vb; if (A->x != B->x) return A->x - B->x; else return A->y - B->y; } // Vektorový rozdíl struct bod rozdil(struct bod A, struct bod B) { return (struct bod) { A.x-B.x, A.y-B.y, -1 }; } double vektorovy_soucin(struct bod u, struct bod v) { return u.x*v.y - u.y*v.x; } // Na které straně od přímky AB leží bod X? double strana_primky(struct bod X, struct bod A, struct bod B) { return vektorovy_soucin(rozdil(X,A), rozdil(B,A)); } // Setřídit body v pořadí, v jakém by ležely na konvexním obalu. // Předpokládá (a neověřuje), že body jsou v konvexní poloze. void konvexni_poradi() { qsort(body, N, sizeof(struct bod), porovnej_body); int o = 0; obal[o++] = 0; // Najde dolní polovinu konvexního pořadí, tvořenou body napravo // od spojnice nejlevějšího a nejpravějšího bodu. for (int i = 1; i < N-1; i++) { if (strana_primky(body[i], body[0], body[N-1]) > 0) { obal[o++] = i; } } obal[o++] = N-1; // Najde horní polovinu konvexního pořadí, tvořenou body nalevo // od spojnice nejlevějšího a nejpravějšího bodu. for (int i = N-1; i > 0; i--) { if (strana_primky(body[i], body[0], body[N-1]) < 0) { obal[o++] = i; } } printf("Konvexní pořadí: "); for (int i=0; i < N; i++) printf(" %d", body[obal[i]].index); printf("\n"); assert(o == N); } void ohodnot_usek(int a, int b) { if (a == b) { // triviální úsek délky 0 D[a][b][0] = 0; D[a][b][1] = 1; P[a][b][0] = -1; P[a][b][1] = -1; } else { double d1 = dist(a, a+1) + D[modN(a+1)][b][0]; double d2 = dist(a, b) + D[modN(a+1)][b][1]; if (d1 < d2) { D[a][b][0] = d1; P[a][b][0] = 0; } else { D[a][b][0] = d2; P[a][b][0] = 1; } fprintf(stderr, "D[%d][%d][0] = %lf\n", a, b, D[a][b][0]); double d3 = dist(b, b-1) + D[a][modN(b-1)][1]; double d4 = dist(b, a) + D[a][modN(b-1)][0]; if (d3 < d4) { D[a][b][1] = d3; P[a][b][1] = 0; } else { D[a][b][1] = d4; P[a][b][1] = 1; } fprintf(stderr, "D[%d][%d][1] = %lf\n", a, b, D[a][b][1]); } } void ohodnot_useky() { // Postupně vyhodnocujeme D[] a P[] pro úseky rostoucích délek for (int delka = 1; delka <= N-1; delka++) { for (int a = 0; a < N; a++) { int b = modN(a + delka); ohodnot_usek(a, b); } } } void vypis_cestu(int a, int b, int p) { while (a != b) { int new_p = p ^ P[a][b][p]; if (p == 0) { printf(" %d", body[obal[a]].index); a = modN(a+1); } else { printf(" %d", body[obal[b]].index); b = modN(b-1); } p = new_p; } printf(" %d", body[obal[a]].index); printf("\n"); } void main() { scanf("%d", &N); for (int i=0; i