-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
working on part 2, slow but seems ok
- Loading branch information
Showing
2 changed files
with
252 additions
and
12 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,252 @@ | ||
import "stdlib/str.jou" | ||
import "stdlib/mem.jou" | ||
import "stdlib/io.jou" | ||
|
||
|
||
def add_computer_if_not_exist(names: byte[3]*, ncomputers: int*, name: byte*) -> None: | ||
for i = 0; i < *ncomputers; i++: | ||
if strcmp(names[i], name) == 0: | ||
return | ||
|
||
assert strlen(name) == 2 | ||
strcpy(names[(*ncomputers)++], name) | ||
|
||
|
||
def find_computer(names: byte[3]*, ncomputers: int, name: byte*) -> int: | ||
for i = 0; i < ncomputers; i++: | ||
if strcmp(names[i], name) == 0: | ||
return i | ||
assert False | ||
|
||
|
||
# If conn_matrix[i][j] is True, then computers i and j are connected | ||
def create_conn_matrix(ncomputers: int) -> bool**: | ||
conn_matrix: bool** = malloc(sizeof(conn_matrix[0]) * ncomputers) | ||
assert conn_matrix != NULL | ||
|
||
for i = 0; i < ncomputers; i++: | ||
conn_matrix[i] = calloc(sizeof(conn_matrix[i][0]), ncomputers) | ||
assert conn_matrix[i] != NULL | ||
|
||
return conn_matrix | ||
|
||
|
||
def sort_names(names: byte[3]*, len: int) -> None: | ||
# very dumb algorithm | ||
while True: | ||
did_something = False | ||
for i = 1; i < len; i++: | ||
if strcmp(names[i-1], names[i]) > 0: | ||
memswap(&names[i-1], &names[i], sizeof(names[0])) | ||
did_something = True | ||
if not did_something: | ||
break | ||
|
||
|
||
def sort_group(group: int*) -> None: | ||
# very dumb algorithm | ||
while True: | ||
did_something = False | ||
for i = 1; group[i] != -1; i++: | ||
if group[i-1] > group[i]: | ||
memswap(&group[i-1], &group[i], sizeof(group[0])) | ||
did_something = True | ||
if not did_something: | ||
break | ||
|
||
|
||
def group_contains(group: int*, value: int) -> bool: | ||
for i = 0; group[i] != -1; i++: | ||
if group[i] == value: | ||
return True | ||
return False | ||
|
||
|
||
def groups_are_equal(g1: int*, g2: int*) -> bool: | ||
for i = 0; g1[i] != -1 or g2[i] != -1; i++: | ||
if g1[i] != g2[i]: | ||
return False | ||
return True | ||
|
||
|
||
# Merges two groups of size n if: | ||
# - they have n-1 same elements in common | ||
# - the two non-common elements (one from each group) are connected | ||
# | ||
# Assumes groups are sorted. | ||
def merge_two_groups(group1: int*, group2: int*, conn_matrix: bool**) -> int*: | ||
n = 0 | ||
while group1[n] != -1 and group2[n] != -1: | ||
n++ | ||
assert group1[n] == -1 | ||
assert group2[n] == -1 | ||
|
||
for i = 1; i < n; i++: | ||
assert group1[i-1] < group1[i] | ||
assert group2[i-1] < group2[i] | ||
|
||
# Find the elements that is only in group 1 or 2. If there are multiple, don't merge. | ||
p1 = group1 | ||
p2 = group2 | ||
only1 = -1 | ||
only2 = -1 | ||
|
||
while *p1 != -1 or *p2 != -1: | ||
if *p2 == -1 or (*p1 != -1 and *p2 != -1 and *p1 < *p2): | ||
# Group 1 left behind, it has element which is not in group 2 | ||
if only1 != -1: | ||
# Not the first one, refuse to merge | ||
return NULL | ||
only1 = *p1++ | ||
elif *p1 == -1 or (*p1 != -1 and *p2 != -1 and *p1 > *p2): | ||
# Group 2 element missing from group 1 | ||
if only2 != -1: | ||
# Not the first one, refuse to merge | ||
return NULL | ||
only2 = *p2++ | ||
else: | ||
# Skip common element | ||
assert *p1 != -1 | ||
assert *p2 != -1 | ||
assert *p1 == *p2 | ||
p1++ | ||
p2++ | ||
|
||
assert p1 == &group1[n] | ||
assert p2 == &group2[n] | ||
|
||
# They shouldn't have everything in common, then they would be the same group | ||
assert only1 != -1 | ||
assert only2 != -1 | ||
|
||
if not conn_matrix[only1][only2]: | ||
return NULL | ||
|
||
result: int* = malloc(sizeof(result[0]) * (n+2)) | ||
assert result != NULL | ||
memcpy(result, group1, sizeof(result[0]) * n) | ||
result[n] = only2 | ||
result[n+1] = -1 | ||
return result | ||
|
||
|
||
# verbose parameter is useful when you're bored and you run this on the real input. | ||
# It is very slow... :) | ||
def merge_all_mergeable_pairs(conn_matrix: bool**, old_groups: int**) -> int**: | ||
n = 0 | ||
while old_groups[n] != NULL: | ||
n++ | ||
printf("Merging %d groups together", n) | ||
fflush(stdout) | ||
|
||
result: int** = malloc(sizeof(result[0])) | ||
assert result != NULL | ||
result_len = 0 | ||
result_alloc = 1 | ||
|
||
for g1 = old_groups; *g1 != NULL; g1++: | ||
for g2 = &g1[1]; *g2 != NULL; g2++: | ||
m = merge_two_groups(*g1, *g2, conn_matrix) | ||
if m == NULL: | ||
continue | ||
|
||
sort_group(m) | ||
|
||
already_exists = False | ||
for i = 0; i < result_len; i++: | ||
if groups_are_equal(result[i], m): | ||
already_exists = True | ||
break | ||
|
||
if already_exists: | ||
free(m) | ||
else: | ||
if result_len == result_alloc: | ||
result_alloc *= 2 | ||
result = realloc(result, sizeof(result[0]) * (result_alloc + 1)) | ||
assert result != NULL | ||
|
||
result[result_len++] = m | ||
|
||
# Print a dot every 100 successful merges. | ||
# Helpful with actual input because it is really slow :) | ||
if result_len % 100 == 0: | ||
printf(".") | ||
fflush(stdout) | ||
|
||
printf("\n") | ||
fflush(stdout) | ||
|
||
result[result_len] = NULL | ||
return result | ||
|
||
|
||
def main() -> int: | ||
names: byte[3]* = malloc(sizeof(names[0]) * 5000) | ||
assert names != NULL | ||
ncomputers = 0 | ||
|
||
f = fopen("input", "r") | ||
assert f != NULL | ||
|
||
name1: byte[3] | ||
name2: byte[3] | ||
while fscanf(f, "%2s-%2s\n", name1, name2) == 2: | ||
assert ncomputers < 5000 | ||
add_computer_if_not_exist(names, &ncomputers, name1) | ||
assert ncomputers < 5000 | ||
add_computer_if_not_exist(names, &ncomputers, name2) | ||
|
||
sort_names(names, ncomputers) | ||
|
||
rewind(f) | ||
conn_matrix = create_conn_matrix(ncomputers) | ||
while fscanf(f, "%2s-%2s\n", name1, name2) == 2: | ||
i1 = find_computer(names, ncomputers, name1) | ||
i2 = find_computer(names, ncomputers, name2) | ||
conn_matrix[i1][i2] = True | ||
conn_matrix[i2][i1] = True | ||
|
||
fclose(f) | ||
|
||
# Create one group for each computer. | ||
groups: int** = malloc(sizeof(groups[0]) * (ncomputers + 1)) | ||
assert groups != NULL | ||
for i = 0; i < ncomputers; i++: | ||
groups[i] = malloc(sizeof(groups[i][0]) * (ncomputers + 1)) | ||
groups[i][0] = i | ||
groups[i][1] = -1 | ||
groups[ncomputers] = NULL | ||
|
||
# Merge until there are at least two groups. | ||
while groups[0] != NULL and groups[1] != NULL: | ||
# Output: Merging 16 groups together | ||
# Output: Merging 32 groups together | ||
# Output: Merging 12 groups together | ||
groups2 = merge_all_mergeable_pairs(conn_matrix, groups) | ||
|
||
for i = 0; groups[i] != NULL; i++: | ||
free(groups[i]) | ||
free(groups) | ||
groups = groups2 | ||
|
||
# There should be one group remaining | ||
assert groups[0] != NULL | ||
|
||
# Output: co-de-ka-ta | ||
for i = 0; groups[0][i] != -1; i++: | ||
if i != 0: | ||
printf("-") | ||
printf("%s", names[groups[0][i]]) | ||
printf("\n") | ||
|
||
free(groups[0]) | ||
free(groups) | ||
|
||
for i = 0; i < ncomputers; i++: | ||
free(conn_matrix[i]) | ||
free(conn_matrix) | ||
|
||
free(names) | ||
|
||
return 0 |