370 lines
12 KiB
C
370 lines
12 KiB
C
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <stdint.h>
|
|
#include <limits.h>
|
|
|
|
#define LINE_MAX_LENGTH 256
|
|
#define MAX_ENTRIES 2048
|
|
#define MAX_QUEUE_LEN 256
|
|
#define MAX_EDGES 1024
|
|
#define MAX_BREAKPOINTS 512
|
|
#define min(a,b) ((a) < (b) ? (a) : (b))
|
|
#define max(a,b) ((a) > (b) ? (a) : (b))
|
|
|
|
typedef struct entry {
|
|
char direction;
|
|
long long count;
|
|
char rgb_string[10];
|
|
} entry_t;
|
|
|
|
typedef struct zone {
|
|
long long upper_left_corner[2];
|
|
long long lower_right_corner[2];
|
|
uint8_t outside;
|
|
} zone_t;
|
|
|
|
int cmp(const void* a, const void* b);
|
|
int on_edge(long long edges[MAX_EDGES][2][2], int edges_num, long long side[2][2]);
|
|
unsigned long long lake_volume(entry_t entries[], int entries_num);
|
|
void decode_rgbs(entry_t entries[], int entries_num);
|
|
|
|
int main() {
|
|
char *p, *buf, c;
|
|
|
|
buf = (char *)malloc(LINE_MAX_LENGTH);
|
|
memset(buf, 0, LINE_MAX_LENGTH);
|
|
p = buf;
|
|
|
|
entry_t entries[MAX_ENTRIES];
|
|
memset(entries, 0, MAX_ENTRIES * sizeof(entry_t));
|
|
int entries_num = 0;
|
|
|
|
while ((c = getchar()) != EOF) {
|
|
*p++ = c;
|
|
if (c == '\n') {
|
|
sscanf(buf, "%c %lli %s", &entries[entries_num].direction, &entries[entries_num].count, entries[entries_num].rgb_string);
|
|
entries_num++;
|
|
memset(buf, 0, LINE_MAX_LENGTH);
|
|
p = buf;
|
|
}
|
|
}
|
|
|
|
unsigned long long part1 = lake_volume(entries, entries_num);
|
|
|
|
decode_rgbs(entries, entries_num);
|
|
|
|
unsigned long long part2 = lake_volume(entries, entries_num);
|
|
|
|
printf("%llu\n", part1);
|
|
printf("%llu\n", part2);
|
|
|
|
free(buf);
|
|
}
|
|
|
|
int cmp(const void* a, const void* b) {
|
|
return *(long long*)a - *(long long*)b;
|
|
}
|
|
|
|
int on_edge(long long edges[MAX_EDGES][2][2], int edges_num, long long side[2][2]) {
|
|
for (int i = 0; i < edges_num; i++) {
|
|
if (edges[i][0][0] == side[0][0] && edges[i][0][0] == edges[i][1][0] && side[0][0] == side[1][0] && edges[i][0][1] <= side[0][1] && edges[i][1][1] >= side[1][1]) {
|
|
return 1;
|
|
}
|
|
if (edges[i][0][1] == side[0][1] && edges[i][0][1] == edges[i][1][1] && side[0][1] == side[1][1] && edges[i][0][0] <= side[0][0] && edges[i][1][0] >= side[1][0]) {
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
unsigned long long lake_volume(entry_t entries[], int entries_num) {
|
|
long long x = 0, y = 0, dir[2];
|
|
|
|
long long edges[MAX_EDGES][2][2];
|
|
memset(edges, 0, MAX_EDGES * 2 * 2 * sizeof(long long));
|
|
int edges_num = 0;
|
|
|
|
for (int i = 0; i < entries_num; i++) {
|
|
switch (entries[i].direction) {
|
|
case 'R':
|
|
dir[0] = 1;
|
|
dir[1] = 0;
|
|
edges[edges_num][0][0] = x;
|
|
edges[edges_num][0][1] = y;
|
|
edges[edges_num][1][0] = x + entries[i].count * dir[0];
|
|
edges[edges_num][1][1] = y + entries[i].count * dir[1];
|
|
edges_num++;
|
|
break;
|
|
case 'L':
|
|
dir[0] = -1;
|
|
dir[1] = 0;
|
|
edges[edges_num][1][0] = x;
|
|
edges[edges_num][1][1] = y;
|
|
edges[edges_num][0][0] = x + entries[i].count * dir[0];
|
|
edges[edges_num][0][1] = y + entries[i].count * dir[1];
|
|
edges_num++;
|
|
break;
|
|
case 'U':
|
|
dir[0] = 0;
|
|
dir[1] = -1;
|
|
edges[edges_num][1][0] = x;
|
|
edges[edges_num][1][1] = y;
|
|
edges[edges_num][0][0] = x + entries[i].count * dir[0];
|
|
edges[edges_num][0][1] = y + entries[i].count * dir[1];
|
|
edges_num++;
|
|
break;
|
|
case 'D':
|
|
dir[0] = 0;
|
|
dir[1] = 1;
|
|
edges[edges_num][0][0] = x;
|
|
edges[edges_num][0][1] = y;
|
|
edges[edges_num][1][0] = x + entries[i].count * dir[0];
|
|
edges[edges_num][1][1] = y + entries[i].count * dir[1];
|
|
edges_num++;
|
|
break;
|
|
}
|
|
|
|
x += entries[i].count * dir[0];
|
|
y += entries[i].count * dir[1];
|
|
}
|
|
|
|
// Find smallest and largest x and y
|
|
long long min_x = LLONG_MAX, max_x = LLONG_MIN, min_y = LLONG_MAX, max_y = LLONG_MIN;
|
|
for (int i = 0; i < edges_num; i++) {
|
|
if (edges[i][0][0] < min_x) {
|
|
min_x = edges[i][0][0];
|
|
}
|
|
if (edges[i][1][0] > max_x) {
|
|
max_x = edges[i][1][0];
|
|
}
|
|
if (edges[i][0][1] < min_y) {
|
|
min_y = edges[i][0][1];
|
|
}
|
|
if (edges[i][1][1] > max_y) {
|
|
max_y = edges[i][1][1];
|
|
}
|
|
}
|
|
|
|
// X breakpoints
|
|
long long x_breakpoints[MAX_BREAKPOINTS], x_breakpoints_num = 0;
|
|
for (int i = 0; i < edges_num; i++) {
|
|
int found = 0;
|
|
for (int j = 0; j < x_breakpoints_num; j++) {
|
|
if (edges[i][0][0] == x_breakpoints[j]) {
|
|
found = 1;
|
|
break;
|
|
}
|
|
}
|
|
if (!found) {
|
|
x_breakpoints[x_breakpoints_num] = edges[i][0][0];
|
|
x_breakpoints_num++;
|
|
}
|
|
found = 0;
|
|
for (int j = 0; j < x_breakpoints_num; j++) {
|
|
if (edges[i][1][0] == x_breakpoints[j]) {
|
|
found = 1;
|
|
break;
|
|
}
|
|
}
|
|
if (!found) {
|
|
x_breakpoints[x_breakpoints_num] = edges[i][1][0];
|
|
x_breakpoints_num++;
|
|
}
|
|
}
|
|
qsort(x_breakpoints, x_breakpoints_num, sizeof(x_breakpoints[0]), &cmp);
|
|
|
|
// Y breakpoints
|
|
long long y_breakpoints[MAX_BREAKPOINTS], y_breakpoints_num = 0;
|
|
for (int i = 0; i < edges_num; i++) {
|
|
int found = 0;
|
|
for (int j = 0; j < y_breakpoints_num; j++) {
|
|
if (edges[i][0][1] == y_breakpoints[j]) {
|
|
found = 1;
|
|
break;
|
|
}
|
|
}
|
|
if (!found) {
|
|
y_breakpoints[y_breakpoints_num] = edges[i][0][1];
|
|
y_breakpoints_num++;
|
|
}
|
|
found = 0;
|
|
for (int j = 0; j < y_breakpoints_num; j++) {
|
|
if (edges[i][1][1] == y_breakpoints[j]) {
|
|
found = 1;
|
|
break;
|
|
}
|
|
}
|
|
if (!found) {
|
|
y_breakpoints[y_breakpoints_num] = edges[i][1][1];
|
|
y_breakpoints_num++;
|
|
}
|
|
}
|
|
qsort(y_breakpoints, y_breakpoints_num, sizeof(y_breakpoints[0]), &cmp);
|
|
|
|
zone_t **zones = (zone_t**)malloc((x_breakpoints_num - 1) * sizeof(zone_t*));
|
|
for (int i = 0; i < x_breakpoints_num - 1; i++) {
|
|
zones[i] = (zone_t*)malloc((y_breakpoints_num - 1) * sizeof(zone_t));
|
|
memset(zones[i], 0, (y_breakpoints_num - 1) * sizeof(zone_t));
|
|
}
|
|
zone_t *curr;
|
|
|
|
for (int i = 0; i < y_breakpoints_num - 1; i++) {
|
|
for (int j = 0; j < x_breakpoints_num - 1; j++) {
|
|
curr = &zones[j][i];
|
|
curr->upper_left_corner[0] = x_breakpoints[j];
|
|
curr->upper_left_corner[1] = y_breakpoints[i];
|
|
curr->lower_right_corner[0] = x_breakpoints[j + 1];
|
|
curr->lower_right_corner[1] = y_breakpoints[i + 1];
|
|
}
|
|
}
|
|
|
|
// Mark zones as outside
|
|
int found = 1;
|
|
while (found) {
|
|
found = 0;
|
|
for (int i = 0; i < y_breakpoints_num - 1; i++) {
|
|
for (int j = 0; j < x_breakpoints_num - 1; j++) {
|
|
if (zones[j][i].outside) {
|
|
continue;
|
|
}
|
|
// LEFT
|
|
long long side[2][2];
|
|
side[0][0] = zones[j][i].upper_left_corner[0];
|
|
side[0][1] = zones[j][i].upper_left_corner[1];
|
|
side[1][0] = zones[j][i].upper_left_corner[0];
|
|
side[1][1] = zones[j][i].lower_right_corner[1];
|
|
if (j == 0 && !on_edge(edges, edges_num, side)) {
|
|
zones[j][i].outside = 1;
|
|
found = 1;
|
|
}
|
|
if (j > 0 && zones[j-1][i].outside && !on_edge(edges, edges_num, side)) {
|
|
zones[j][i].outside = 1;
|
|
found = 1;
|
|
}
|
|
// RIGHT
|
|
side[0][0] = zones[j][i].lower_right_corner[0];
|
|
side[0][1] = zones[j][i].upper_left_corner[1];
|
|
side[1][0] = zones[j][i].lower_right_corner[0];
|
|
side[1][1] = zones[j][i].lower_right_corner[1];
|
|
if (j == x_breakpoints_num - 2 && !on_edge(edges, edges_num, side)) {
|
|
zones[j][i].outside = 1;
|
|
found = 1;
|
|
}
|
|
if (j < x_breakpoints_num - 2 && zones[j+1][i].outside && !on_edge(edges, edges_num, side)) {
|
|
zones[j][i].outside = 1;
|
|
found = 1;
|
|
}
|
|
// UP
|
|
side[0][0] = zones[j][i].upper_left_corner[0];
|
|
side[0][1] = zones[j][i].upper_left_corner[1];
|
|
side[1][0] = zones[j][i].lower_right_corner[0];
|
|
side[1][1] = zones[j][i].upper_left_corner[1];
|
|
if (i == 0 && !on_edge(edges, edges_num, side)) {
|
|
zones[j][i].outside = 1;
|
|
found = 1;
|
|
}
|
|
if (i > 0 && zones[j][i-1].outside && !on_edge(edges, edges_num, side)) {
|
|
zones[j][i].outside = 1;
|
|
found = 1;
|
|
}
|
|
// DOWN
|
|
side[0][0] = zones[j][i].upper_left_corner[0];
|
|
side[0][1] = zones[j][i].lower_right_corner[1];
|
|
side[1][0] = zones[j][i].lower_right_corner[0];
|
|
side[1][1] = zones[j][i].lower_right_corner[1];
|
|
if (i == y_breakpoints_num - 2 && !on_edge(edges, edges_num, side)) {
|
|
zones[j][i].outside = 1;
|
|
found = 1;
|
|
}
|
|
if (i < y_breakpoints_num - 2 && zones[j][i+1].outside && !on_edge(edges, edges_num, side)) {
|
|
zones[j][i].outside = 1;
|
|
found = 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
unsigned long long sum = 0;
|
|
unsigned long long out = 0;
|
|
for (int i = 0; i < y_breakpoints_num - 1; i++) {
|
|
for (int j = 0; j < x_breakpoints_num - 1; j++) {
|
|
if (zones[j][i].outside) {
|
|
out++;
|
|
}
|
|
}
|
|
}
|
|
|
|
for (int i = 0; i < y_breakpoints_num - 1; i++) {
|
|
for (int j = 0; j < x_breakpoints_num - 1; j++) {
|
|
if (zones[j][i].outside) {
|
|
continue;
|
|
}
|
|
sum += (zones[j][i].lower_right_corner[0] - zones[j][i].upper_left_corner[0] - 1) * (zones[j][i].lower_right_corner[1] - zones[j][i].upper_left_corner[1] - 1);
|
|
// RIGHT
|
|
long long side[2][2];
|
|
side[0][0] = zones[j][i].lower_right_corner[0];
|
|
side[0][1] = zones[j][i].upper_left_corner[1];
|
|
side[1][0] = zones[j][i].lower_right_corner[0];
|
|
side[1][1] = zones[j][i].lower_right_corner[1];
|
|
if (!on_edge(edges, edges_num, side)) {
|
|
sum += side[1][1] - side[0][1] - 1;
|
|
}
|
|
// DOWN
|
|
side[0][1] = zones[j][i].lower_right_corner[1];
|
|
side[0][0] = zones[j][i].upper_left_corner[0];
|
|
side[1][0] = zones[j][i].lower_right_corner[0];
|
|
side[1][1] = zones[j][i].lower_right_corner[1];
|
|
if (!on_edge(edges, edges_num, side)) {
|
|
sum += side[1][0] - side[0][0] - 1;
|
|
}
|
|
// CORNER
|
|
side[0][1] = zones[j][i].lower_right_corner[1];
|
|
side[0][0] = zones[j][i].lower_right_corner[0];
|
|
side[1][0] = zones[j][i].lower_right_corner[0];
|
|
side[1][1] = zones[j][i].lower_right_corner[1];
|
|
if (!on_edge(edges, edges_num, side)) {
|
|
sum++;
|
|
}
|
|
}
|
|
}
|
|
|
|
for (int i = 0; i < edges_num; i++) {
|
|
sum += (edges[i][1][0] - edges[i][0][0]) + (edges[i][1][1] - edges[i][0][1]);
|
|
}
|
|
|
|
for (int i = 0; i < x_breakpoints_num - 1; i++) {
|
|
free(zones[i]);
|
|
}
|
|
free(zones);
|
|
|
|
return sum;
|
|
}
|
|
|
|
void decode_rgbs(entry_t entries[], int entries_num) {
|
|
for (int i = 0; i < entries_num; i++) {
|
|
char value[6], *p;
|
|
memset(value, 0, 6);
|
|
p = entries[i].rgb_string;
|
|
while(*p != '#') p++;
|
|
p++;
|
|
strncpy(value, p, 5);
|
|
sscanf(value, "%llx", &entries[i].count);
|
|
switch(entries[i].rgb_string[7]) {
|
|
case '0':
|
|
entries[i].direction = 'R';
|
|
break;
|
|
case '1':
|
|
entries[i].direction = 'D';
|
|
break;
|
|
case '2':
|
|
entries[i].direction = 'L';
|
|
break;
|
|
case '3':
|
|
entries[i].direction = 'U';
|
|
break;
|
|
}
|
|
}
|
|
}
|