Cầu những khoảng trống


14

Đưa ra một hình ảnh đen trắng với nền trắng và một tập hợp các chấm đen, vẽ một tập hợp các pixel trắng màu đỏ, sao cho có một đường dẫn giữa mỗi cặp pixel đen.

Chi tiết

  • Đường dẫn là một tập hợp các pixel được kết nối (kết nối 8 vùng lân cận). Pixel đen có thể được sử dụng như một phần của đường dẫn. Mục tiêu là cố gắng giảm thiểu tập hợp các pixel màu đỏ trong các điều kiện trên và xuất ra một hình ảnh tương ứng.

  • Bạn không cần phải tìm giải pháp tối ưu.

  • Một giải pháp tầm thường và đồng thời tồi tệ nhất chỉ là sơn tất cả các pixel trắng màu đỏ.

  • Ví dụ (Pixels được phóng to để hiển thị):

Chi tiết

  • Đưa ra một hình ảnh pixel (ở bất kỳ định dạng phù hợp nào) trả về một hình ảnh khác có các chấm được kết nối như được chỉ định ở trên, cũng như một số nguyên cho biết có bao nhiêu pixel màu đỏ đã được sử dụng.
  • Điểm số là sản phẩm của (1 + số pixel màu đỏ) cho mỗi trong số 14 mẫu thử.
  • Mục tiêu là có điểm thấp nhất.

Tủ thử

14 testcase được hiển thị dưới đây. Một chương trình python để xác minh tính kết nối của các đầu ra có thể được tìm thấy ở đây.

Tổng hợp

Cảm ơn @Veskah, @Firthize, @ wizzwizz4 và @trichoplax cho các đề xuất khác nhau.


1
Thử thách tốt; Tôi thích những người có kế hoạch chấm điểm khác nhau và sáng tạo. Tôi giả sử chương trình cần phải làm việc trên một hình ảnh tùy ý, không chỉ 14 ví dụ cụ thể này? Nếu vậy, chúng ta có thể giả sử kích thước tối đa hợp lý, như 512x512 cho mỗi hình ảnh Mona Lisa hoặc 1024x1024 không?
BradC

Cảm ơn vì bạn đã phản hồi! Có, bạn có thể giả sử kích thước tối đa (cũng là kích thước tối thiểu nếu cần thiết), miễn là tất cả 14 ví dụ có thể được xử lý.
flawr

Làm thế nào để tôi chuyển đổi png thành ascii hoặc json hoặc một cái gì đó dễ dàng để phân tích?
ngn

Bạn có phải tính điểm của riêng bạn không? Một chương trình thử mọi sự kết hợp có thể của các pixel trắng để tô màu đỏ và xem tập hợp con nào có ít pixel đỏ nhất trong khi kết nối tất cả các pixel đen sẽ có điểm số cao nhất có thể, nhưng nó sẽ chậm đến mức mất nhiều thời gian hơn của vũ trụ để thực sự tính điểm đó.
Leo Tenenbaum

1
@ngn Mở trong GIMP, lưu dưới định dạng netpbm.
wizzwizz4

Câu trả lời:


7

C, điểm 2.394x10 ^ 38

Người đàn ông này mất quá nhiều thời gian để làm, rất có thể là do sự lựa chọn ngôn ngữ của tôi. Tôi đã nhận được thuật toán hoạt động khá sớm, nhưng gặp rất nhiều vấn đề với việc cấp phát bộ nhớ (không thể đệ quy các công cụ miễn phí do tràn ngăn xếp, kích thước rò rỉ là rất lớn).

Vẫn! Nó đánh bại mục khác trên mọi trường hợp thử nghiệm và thậm chí có thể là tối ưu khá gần hoặc chính xác các giải pháp tối ưu rất nhiều thời gian.

Dù sao, đây là mã:

#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>
#include <string.h>

#define WHITE 'W'
#define BLACK 'B'
#define RED   'R'


typedef struct image {
    int w, h;
    char* buf;
} image;

typedef struct point {
    int x, y;
    struct point *next;
    struct point *parent;
} point;

typedef struct shape {
    point* first_point;
    point* last_point;

    struct shape* next_shape;
} shape;


typedef struct storage {
    point* points;
    size_t points_size;
    size_t points_index;

    shape* shapes;
    size_t shapes_size;
    size_t shapes_index;
} storage;

char getpx(image* img, int x, int y) {
    if (0>x || x>=img->w || 0>y || y>=img->h) {
        return WHITE;
    } else {
        return img->buf[y*img->w+x];
    }
}

storage* create_storage(int w, int h) {
    storage* ps = (storage*)malloc(sizeof(storage));

    ps->points_size = 8*w*h;
    ps->points = (point*)calloc(ps->points_size, sizeof(point));
    ps->points_index = 0;

    ps->shapes_size = 2*w*h;
    ps->shapes = (shape*)calloc(ps->shapes_size, sizeof(shape));
    ps->shapes_index = 0;

    return ps;
}

void free_storage(storage* ps) {
    if (ps != NULL) {
        if (ps->points != NULL) {
            free(ps->points);
            ps->points = NULL;
        }
        if (ps->shapes != NULL) {
            free(ps->shapes);
            ps->shapes = NULL;
        }
        free(ps);
    }
}


point* alloc_point(storage* ps) {
    if (ps->points_index == ps->points_size) {
        printf("WHOAH THERE BUDDY SLOW DOWN\n");
        /*// double the size of the buffer
        point* new_buffer = (point*)malloc(ps->points_size*2*sizeof(point));
        // need to change all existing pointers to point to new buffer
        long long int pointer_offset = (long long int)new_buffer - (long long int)ps->points;
        for (size_t i=0; i<ps->points_index; i++) {
            new_buffer[i] = ps->points[i];
            if (new_buffer[i].next != NULL) {
                new_buffer[i].next += pointer_offset;
            }
            if (new_buffer[i].parent != NULL) {
                new_buffer[i].parent += pointer_offset;
            }
        }

        for(size_t i=0; i<ps->shapes_index; i++) {
            if (ps->shapes[i].first_point != NULL) {
                ps->shapes[i].first_point += pointer_offset;
            }
            if (ps->shapes[i].last_point != NULL) {
                ps->shapes[i].last_point += pointer_offset;
            }
        }

        free(ps->points);
        ps->points = new_buffer;
        ps->points_size = ps->points_size * 2;*/
    }
    point* out = &(ps->points[ps->points_index]);
    ps->points_index += 1;
    return out;
}

shape* alloc_shape(storage* ps) {
    /*if (ps->shapes_index == ps->shapes_size) {
        // double the size of the buffer
        shape* new_buffer = (shape*)malloc(ps->shapes_size*2*sizeof(shape));
        long long int pointer_offset = (long long int)new_buffer - (long long int)ps->shapes;
        for (size_t i=0; i<ps->shapes_index; i++) {
            new_buffer[i] = ps->shapes[i];
            if (new_buffer[i].next_shape != NULL) {
                new_buffer[i].next_shape += pointer_offset;
            }
        }
        free(ps->shapes);
        ps->shapes = new_buffer;
        ps->shapes_size = ps->shapes_size * 2;
    }*/
    shape* out = &(ps->shapes[ps->shapes_index]);
    ps->shapes_index += 1;
    return out;
}

shape floodfill_shape(image* img, storage* ps, int x, int y, char* buf) {
    // not using point allocator for exploration stack b/c that will overflow it

    point* stack = (point*)malloc(sizeof(point));
    stack->x = x;
    stack->y = y;
    stack->next = NULL;
    stack->parent = NULL;

    point* explored = NULL;
    point* first_explored;
    point* next_explored;

    while (stack != NULL) {
        int sx = stack->x;
        int sy = stack->y;
        point* prev_head = stack;
        stack = stack->next;
        free(prev_head);

        buf[sx+sy*img->w] = 1; // mark as explored

        // add point to shape
        next_explored = alloc_point(ps);
        next_explored->x = sx;
        next_explored->y = sy;
        next_explored->next = NULL;
        next_explored->parent = NULL;

        if (explored != NULL) {
            explored->next = next_explored;
        } else {
            first_explored = next_explored;
        }
        explored = next_explored;

        for (int dy=-1; dy<2; dy++) {
        for (int dx=-1; dx<2; dx++) {
            if (dy != 0 || dx != 0) {
                int nx = sx+dx;
                int ny = sy+dy;
                if (getpx(img, nx, ny) == WHITE || buf[nx+ny*img->w]) {
                    // skip adding point to fringe
                } else {
                    // push point to top of stack
                    point* new_point = (point*)malloc(sizeof(point));
                    new_point->x = nx;
                    new_point->y = ny;
                    new_point->next = stack;
                    new_point->parent = NULL;

                    stack = new_point;
                } 
            }
        }
        }
    }

    /*if (getpx(img, x, y) == WHITE || buf[x+y*img->w]) {
        return (shape){NULL, NULL, NULL};
    } else {
        buf[x+y*img->w] = 1;

        shape e  = floodfill_shape(img, ps, x+1, y,   buf);
        shape ne = floodfill_shape(img, ps, x+1, y+1, buf);
        shape n  = floodfill_shape(img, ps, x,   y+1, buf);
        shape nw = floodfill_shape(img, ps, x-1, y+1, buf);
        shape w  = floodfill_shape(img, ps, x-1, y,   buf);
        shape sw = floodfill_shape(img, ps, x-1, y-1, buf);
        shape s  = floodfill_shape(img, ps, x,   y-1, buf);
        shape se = floodfill_shape(img, ps, x+1, y-1, buf);

        point *p = alloc_point(ps);
        p->x = x;
        p->y = y;
        p->next = NULL;
        p->parent = NULL;

        shape o = (shape){p, p, NULL};
        if (e.first_point != NULL) {
            o.last_point->next = e.first_point;
            o.last_point = e.last_point;
        }
        if (ne.first_point != NULL) {
            o.last_point->next = ne.first_point;
            o.last_point = ne.last_point;
        }
        if (n.first_point != NULL) {
            o.last_point->next = n.first_point;
            o.last_point = n.last_point;
        }
        if (nw.first_point != NULL) {
            o.last_point->next = nw.first_point;
            o.last_point = nw.last_point;
        }
        if (w.first_point != NULL) {
            o.last_point->next = w.first_point;
            o.last_point = w.last_point;
        }
        if (sw.first_point != NULL) {
            o.last_point->next = sw.first_point;
            o.last_point = sw.last_point;
        }
        if (s.first_point != NULL) {
            o.last_point->next = s.first_point;
            o.last_point = s.last_point;
        }
        if (se.first_point != NULL) {
            o.last_point->next = se.first_point;
            o.last_point = se.last_point;
        }

        return o;
    }*/

    shape out = {first_explored, explored, NULL};

    return out;
}

shape* create_shapes(image* img, storage* ps) {
    char* added_buffer = (char*)calloc(img->w*img->h, sizeof(char));
    shape* first_shape = NULL;
    shape* last_shape = NULL;
    int num_shapes = 0;
    for (int y=0; y<img->h; y++) {
        for (int x=0; x<img->w; x++) {
            if (getpx(img, x, y) != WHITE && !(added_buffer[x+y*img->w])) {
                shape* alloced_shape = alloc_shape(ps);
                *alloced_shape = floodfill_shape(img, ps, x, y, added_buffer);

                if (first_shape == NULL) {
                    first_shape = alloced_shape;
                    last_shape = alloced_shape;
                } else if (last_shape != NULL) {
                    last_shape->next_shape = alloced_shape;
                    last_shape = alloced_shape;
                }

                num_shapes++;
            }
        }
    }

    free(added_buffer);

    return first_shape;
}

void populate_buf(image* img, shape* s, char* buf) {
    point* p = s->first_point;

    while (p != NULL) {
        buf[p->x+p->y*img->w] = 1;
        p = p->next;
    }
}

bool expand_frontier(image* img, storage* ps, shape* prev_frontier, shape* next_frontier, char* buf) {
    point* p = prev_frontier->first_point;
    point* n = NULL;

    bool found = false;

    size_t starting_points_index = ps->points_index;

    while (p != NULL) {
        for (int dy=-1; dy<2; dy++) {
        for (int dx=-1; dx<2; dx++) {
            if (dy != 0 || dx != 0) {
                int nx = p->x+dx;
                int ny = p->y+dy;
                if ((0<=nx && nx<img->w && 0<=ny && ny<img->h) // in bounds
                        && !buf[nx+ny*img->w]) {               // not searched yet
                    buf[nx+ny*img->w] = 1;
                    if (getpx(img, nx, ny) != WHITE) {
                        // found a new shape!
                        ps->points_index = starting_points_index;
                        n = alloc_point(ps);
                        n->x = nx;
                        n->y = ny;
                        n->next = NULL;
                        n->parent = p;
                        found = true;
                        goto __expand_frontier_fullbreak;
                    } else {
                        // need to search more
                        point* f = alloc_point(ps);
                        f->x = nx;
                        f->y = ny;
                        f->next = n;
                        f->parent = p;
                        n = f;
                    }
                }
            }
        }}

        p = p->next;
    }
__expand_frontier_fullbreak:
    p = NULL;
    point* last_n = n;
    while (last_n->next != NULL) {
        last_n = last_n->next;
    }

    next_frontier->first_point = n;
    next_frontier->last_point = last_n;

    return found;
}

void color_from_frontier(image* img, point* frontier_point) {
    point* p = frontier_point->parent;

    while (p->parent != NULL) { // if everything else is right,
                                // a frontier point should come in a chain of at least 3
                                // (f point (B) -> point to color (W) -> point in shape (B) -> NULL)
        img->buf[p->x+p->y*img->w] = RED;
        p = p->parent;
    }
}

int main(int argc, char** argv) {
    if (argc < 3) {
        printf("Error: first argument must be filename to load, second argument filename to save to.\n");
        return 1;
    }

    char* fname = argv[1];
    FILE* fp = fopen(fname, "r");

    if (fp == NULL) {
        printf("Error opening file \"%s\"\n", fname);
        return 1;
    }

    int w, h;
    w = 0;
    h = 0;
    fscanf(fp, "%d %d\n", &w, &h);

    if (w==0 || h==0) {
        printf("Error: invalid width/height specified\n");
        return 1;
    }

    char* buf = (char*)malloc(sizeof(char)*w*h+1);
    fgets(buf, w*h+1, fp);
    fclose(fp);

    image img = (image){w, h, buf};

    int nshapes = 0;
    storage* ps = create_storage(w, h);

    while (nshapes != 1) {
        // main loop, do processing step until one shape left
        ps->points_index = 0;
        ps->shapes_index = 0;

        shape* head = create_shapes(&img, ps);
        nshapes = 0;
        shape* pt = head;
        while (pt != NULL) {
            pt = pt->next_shape;
            nshapes++;
        }
        if (nshapes % 1024 == 0) {
            printf("shapes left: %d\n", nshapes);
        }
        if (nshapes == 1) {
            goto __main_task_complete;
        }


        shape* frontier = alloc_shape(ps);
        // making a copy so we can safely free later
        point* p = head->first_point;
        point* ffp = NULL;
        point* flp = NULL;
        while (p != NULL) {
            if (ffp == NULL) {
                ffp = alloc_point(ps);
                ffp->x = p->x;
                ffp->y = p->y;
                ffp->next = NULL;
                ffp->parent = NULL;
                flp = ffp;
            } else {
                point* fnp = alloc_point(ps);
                fnp->x = p->x;
                fnp->y = p->y;
                fnp->next = NULL;
                fnp->parent = NULL;

                flp->next = fnp;
                flp = fnp;
            }

            p = p->next;
        }
        frontier->first_point = ffp;
        frontier->last_point = flp;
        frontier->next_shape = NULL;

        char* visited_buf = (char*)calloc(img.w*img.h+1, sizeof(char));
        populate_buf(&img, frontier, visited_buf);

        shape* new_frontier = alloc_shape(ps);
        new_frontier->first_point = NULL;
        new_frontier->last_point = NULL;
        new_frontier->next_shape = NULL;

        while (!expand_frontier(&img, ps, frontier, new_frontier, visited_buf)) {
            frontier->first_point = new_frontier->first_point;
            frontier->last_point = new_frontier->last_point;
            new_frontier->next_shape = frontier;
        }

        free(visited_buf);
        color_from_frontier(&img, new_frontier->first_point);
__main_task_complete:
        img = img;
    }

    free_storage(ps);

    char* outfname = argv[2];
    fp = fopen(outfname, "w");

    if (fp == NULL) {
        printf("Error opening file \"%s\"\n", outfname);
        return 1;
    }

    fprintf(fp, "%d %d\n", img.w, img.h);
    fprintf(fp, "%s", img.buf);

    free(img.buf);

    fclose(fp);

    return 0;
}

Đã thử nghiệm trên: Arch Linux, GCC 9.1.0, -O3

Mã này nhận đầu vào / đầu ra trong một tệp tùy chỉnh mà tôi gọi là "cppm" (vì nó giống như một phiên bản cô đọng của định dạng PPM cổ điển). Một tập lệnh python để chuyển đổi sang / từ nó bên dưới:

from PIL import Image

BLACK='B'
WHITE='W'
RED  ='R'


def image_to_cppm(infname, outfname):
    outfile = open(outfname, 'w')
    im = Image.open(infname)

    w, h = im.width, im.height
    outfile.write(f"{w} {h}\n")
    for y in range(h):
        for x in range(w):
            r, g, b, *_ = im.getpixel((x, y))
            if r==0 and g==0 and b==0:
                outfile.write(BLACK)
            elif g==0 and b==0:
                outfile.write(RED)
            else:
                outfile.write(WHITE)
    outfile.write("\n")
    outfile.close()
    im.close()

def cppm_to_image(infname, outfname):
    infile = open(infname, 'r')

    w, h = infile.readline().split(" ")
    w, h = int(w), int(h)

    im = Image.new('RGB', (w, h), color=(255, 255, 255))

    for y in range(h):
        for x in range(w):
            c = infile.read(1)
            if c==BLACK:
                im.putpixel((x,y), (0, 0, 0))
            elif c==RED:
                im.putpixel((x,y), (255, 0, 0))

    infile.close()
    im.save(outfname)
    im.close()


if __name__ == "__main__":
    import sys
    if len(sys.argv) < 3:
        print("Error: must provide 2 files to convert, first is from, second is to")

    infname = sys.argv[1]
    outfname = sys.argv[2]

    if not infname.endswith("cppm") and outfname.endswith("cppm"):
        image_to_cppm(infname, outfname)
    elif infname.endswith("cppm") and not outfname.endswith("cppm"):
        cppm_to_image(infname, outfname)
    else:
        print("didn't do anything, exactly one file must end with .cppm")

Giải thích thuật toán

Cách thuật toán này hoạt động là nó bắt đầu bằng cách tìm tất cả các hình dạng được kết nối trong hình ảnh, bao gồm các pixel màu đỏ. Sau đó, nó lấy cái đầu tiên và mở rộng một pixel phía trước của nó tại một thời điểm cho đến khi nó gặp một hình dạng khác. Sau đó, nó tô màu tất cả các pixel từ chạm đến hình dạng ban đầu (sử dụng danh sách liên kết mà nó tạo ra trên đường đi để theo dõi). Cuối cùng, nó lặp lại quá trình, tìm tất cả các hình dạng mới được tạo ra, cho đến khi chỉ còn một hình dạng.

Thư viện hình ảnh

Testcase 1, 183 pixel

kiểm tra 1

Testcase 2, 140 pixel

thử nghiệm 2

Testcase 3, 244 pixel

thử nghiệm 3

Testcase 4, 42 pixel

thử nghiệm 4

Testcase 5, 622 pixel

thử nghiệm 5

Testcase 6, 1 pixel

thử nghiệm 6

Testcase 7, 104 pixel

thử nghiệm 7

Testcase 8, 2286 pixel

thử nghiệm 8

Testcase 9, 22 pixel

thử nghiệm 9

Testcase 10, 31581 pixel

thử nghiệm 10

Testcase 11, 21421 pixel

thử nghiệm 11

Testcase 12, 5465 pixel

thử nghiệm 12

Testcase 13, 4679 pixel

thử nghiệm 13

Testcase 14, 7362 pixel

thử nghiệm 14


2
Công việc tốt đẹp! Có vẻ rất hiệu quả, mặc dù tôi có thể tưởng tượng một vài hình dạng với các giải pháp tối ưu hơn một chút: Ví dụ 3 (4 chấm trong một hình vuông), ví dụ, tôi (thủ công) đã xuống thấp đến 175 (một chữ X màu đỏ), không biết làm thế nào Tôi buộc điều đó thông qua thuật toán.
BradC

6

Con trăn, 2,62 * 10 ^ 40

Thuật toán này chỉ lấp đầy (BFS) mặt phẳng bắt đầu từ các phần màu đen của hình ảnh, trong đó với mỗi pixel mới, chúng tôi ghi lại phần màu đen mà nó bị ngập. Ngay khi chúng ta có hai pixel lân cận với các phần đen khác nhau như tổ tiên, về cơ bản chúng ta hợp nhất hai phần đen này bằng cách nối chúng qua tổ tiên của hai hàng xóm mà chúng ta vừa tìm thấy. Về lý thuyết, điều này có thể được thực hiện trong O(#pixels), nhưng để giữ cho số lượng mã ở mức chấp nhận được thì việc thực hiện này hơi tệ hơn.

Đầu ra

nhập mô tả hình ảnh ở đây nhập mô tả hình ảnh ở đây nhập mô tả hình ảnh ở đây nhập mô tả hình ảnh ở đây nhập mô tả hình ảnh ở đây nhập mô tả hình ảnh ở đây nhập mô tả hình ảnh ở đây nhập mô tả hình ảnh ở đây nhập mô tả hình ảnh ở đây nhập mô tả hình ảnh ở đây nhập mô tả hình ảnh ở đây nhập mô tả hình ảnh ở đây nhập mô tả hình ảnh ở đây nhập mô tả hình ảnh ở đây

import numpy as np
from scipy import ndimage
import imageio
from collections import deque

# path to your image
for k in range(1, 15):
    fname=str(k).zfill(2) +'.png'
    print("processing ", fname)

    # load image
    img = imageio.imread("./images/"+fname, pilmode="RGB")
    print(img.shape)

    # determine non_white part
    white = np.logical_and(np.logical_and(img[:,:,0] == 255, img[:,:,1] == 255), img[:,:,2] == 255)
    non_white = np.logical_not(white)

    # find connected components of non-white part
    neighbourhood = np.ones((3,3))
    labeled, nr_objects = ndimage.label(non_white, neighbourhood)

    # print result
    print("number of separate objects is {}".format(nr_objects))

    # start flood filling algorithm
    ind = np.nonzero(labeled)
    front = deque(zip(ind[0],ind[1]))

    membership = np.copy(labeled)
    is_merge_point = np.zeros_like(labeled) > 0
    parent = np.zeros((2,) + labeled.shape) #find ancestor of each pixel
    is_seed = labeled > 0
    size_i, size_j = labeled.shape
    # flood from every seed
    while front: #while we have unexplored pixels
        point = front.popleft()
        # check neighbours:
        for (di,dj) in [(-1,-1),(-1,0),(-1,1),(0,-1),(0,1),(1,-1),(1,0),(1,1)]:
            current = membership[point[0], point[1]]
            new_i, new_j = point[0]+di, point[1]+dj
            if 0 <= new_i < size_i and 0 <= new_j < size_j:
                value = membership[new_i, new_j]
                if value == 0:
                    membership[new_i, new_j] = current
                    front.append((new_i, new_j))
                    parent[:, new_i, new_j] = point
                elif value != current: #MERGE!
                    is_merge_point[point[0], point[1]] = True
                    is_merge_point[new_i, new_j] = True
                    membership[np.logical_or(membership == value, membership == current)] = min(value, current)

    # trace back from every merger
    ind = np.nonzero(is_merge_point)
    merge_points = deque(zip(ind[0].astype(np.int),ind[1].astype(np.int)))
    for point in merge_points:
        next_p = point
        while not is_seed[next_p[0], next_p[1]]:
            is_merge_point[next_p[0], next_p[1]] = True
            next_p = parent[:, next_p[0], next_p[1]].astype(np.int)

    # add red points:
    img_backup = np.copy(img)
    img[:,:,0][is_merge_point] = 255 * img_backup[:,:,0]
    img[:,:,1][is_merge_point] = 0   * img_backup[:,:,1]
    img[:,:,2][is_merge_point] = 0   * img_backup[:,:,2]

    #compute number of new points
    n_red_points = (img[:,:,0] != img[:,:,1]).sum()
    print("#red points:", n_red_points)

    # plot: each component should have separate color
    imageio.imwrite("./out_images/"+fname, np.array(img))

Ghi bàn

(1+183)*(1+142)*(1+244)*(1+42)*(1+1382)*(1+2)*(1+104)*(1+7936)*(1+26)*(1+38562)*(1+42956)*(1+6939)*(1+8882)*(1+9916)
= 26208700066468930789809050445560539404000
= 2.62 * 10^40

- Cái này, tôi tin là tối ưu. Tốt lắm .-- Được rồi, điều này không tối ưu. Tôi không hiểu tại sao không.
wizzwizz4

@ wizzwizz4 Hãy xem trường hợp dễ dàng của bốn góc của hình vuông: Giải pháp tối ưu sẽ là X. Trong khi về lý thuyết, thuật toán của tôi có thể tìm ra giải pháp này, rất khó xảy ra. Nhiều khả năng nó tìm thấy một giải pháp với ba đường dẫn, mỗi đường nối hai điểm.
flawr

@ wizzwizz4 Yep, phóng to ví dụ văn bản wikipedia và bạn sẽ thấy hàng tấn địa điểm nhỏ nơi đường dẫn kết nối khác nhau sẽ lưu một hoặc hai pixel màu đỏ; họ sẽ thêm vào.
BradC

Nhưng điều này có vẻ giống như bong bóng xà phòng trên chốt, đó là một giải pháp hợp pháp cho vấn đề cây Steiner .
wizzwizz4

1
@ wizzwizz4 Sự khác biệt, sau đó, phải rằng chúng tôi không kết nối điểm , chúng ta đang kết nối bộ điểm, vì vậy chúng tôi không được quyết định mà điểm trong mỗi bộ để kết nối một cách tối ưu. Phóng to ví dụ văn bản một lần nữa, những cải tiến, bạn có thể thấy hầu hết là phải làm với các bộ phận của mỗi hình được kết nối.
BradC

5

Con trăn 3: 1,7x10 ^ 42 1,5x10 ^ 41

Sử dụng Pillow, numpyscipy.

Hình ảnh được cho là nằm trong một imagesthư mục nằm trong cùng thư mục với tập lệnh.

Disclaimer : Phải mất một thời gian dài để xử lý tất cả các hình ảnh.

import sys
import os

from PIL import Image
import numpy as np
import scipy.ndimage


def obtain_groups(image, threshold, structuring_el):
    """
    Obtain isles of unconnected pixels via a threshold on the R channel
    """
    image_logical = (image[:, :, 1] < threshold).astype(np.int)
    return scipy.ndimage.measurements.label(image_logical, structure=structuring_el)


def swap_colors(image, original_color, new_color):
    """
    Swap all the pixels of a specific color by another color 
    """
    r1, g1, b1 = original_color  # RGB value to be replaced
    r2, g2, b2 = new_color  # New RGB value
    red, green, blue = image[:, :, 0], image[:, :, 1], image[:, :, 2]
    mask = (red == r1) & (green == g1) & (blue == b1)
    image[:, :, :3][mask] = [r2, g2, b2]
    return image


def main(image_path=None):
    images = os.listdir("images")
    f = open("results.txt", "w")

    if image_path is not None:
        images = [image_path]

    for image_name in images:
        im = Image.open("images/"+image_name).convert("RGBA")
        image = np.array(im)

        image = swap_colors(image, (255, 255, 255), (255, 0, 0))

        # create structuring element to determine unconnected groups of pixels in image
        s = scipy.ndimage.morphology.generate_binary_structure(2, 2)

        for i in np.ndindex(image.shape[:2]):
            # skip black pixels
            if sum(image[i[0], i[1]]) == 255:
                continue
            image[i[0], i[1]] = [255, 255, 255, 255]
            # label the different groups, considering diagonal connections as valid
            groups, num_groups = obtain_groups(image, 255, s)
            if num_groups != 1:
                image[i[0], i[1]] = [255, 0, 0, 255]
            # Show percentage
            print((i[1] + i[0]*im.size[0])/(im.size[0]*im.size[1]))

        # Number of red pixels
        red_p = 0
        for i in np.ndindex(image.shape[:2]):
            j = (im.size[1] - i[0] - 1, im.size[0] - i[1] - 1)
            # skip black and white pixels
            if sum(image[j[0], j[1]]) == 255 or sum(image[j[0], j[1]]) == 255*4:
                continue
            image[j[0], j[1]] = [255, 255, 255, 255]
            # label the different groups, considering diagonal connections as valid
            groups, num_groups = obtain_groups(image, 255, s)
            if num_groups != 1:
                image[j[0], j[1]] = [255, 0, 0, 255]
            # Show percentage
            print((j[1] + j[0]*im.size[0])/(im.size[0]*im.size[1]))
            red_p += (sum(image[j[0], j[1]]) == 255*2)

        print(red_p)
        f.write("r_"+image_name+": "+str(red_p)+"\n")

        im = Image.fromarray(image)
        im.show()
        im.save("r_"+image_name)
    f.close()


if __name__ == "__main__":
    if len(sys.argv) == 2:
        main(sys.argv[1])
    else:
        main()

Giải trình

Giải pháp tầm thường. Chúng tôi bắt đầu bằng cách thay đổi màu của tất cả các pixel trắng trong ảnh thành màu đỏ. Bằng cách này, đảm bảo rằng tất cả các yếu tố (bất kỳ đảo màu đen pixel nào) được kết nối.

Sau đó, chúng tôi lặp lại tất cả các pixel trong ảnh bắt đầu từ góc trên cùng bên trái và di chuyển sang phải và xuống. Đối với mỗi pixel màu đỏ, chúng tôi thấy chúng tôi thay đổi màu của nó thành màu trắng. Nếu sau khi thay đổi màu này, vẫn chỉ có một yếu tố (một yếu tố hiện là bất kỳ hòn đảo nào có pixel đen và đỏ), chúng ta sẽ để pixel trắng và chuyển sang pixel tiếp theo. Tuy nhiên, nếu sau khi thay đổi màu từ đỏ sang trắng, số lượng phần tử lớn hơn một, chúng ta sẽ để pixel màu đỏ và chuyển sang pixel tiếp theo.

Cập nhật

Như có thể thấy (và dự kiến), các kết nối thu được chỉ bằng cách sử dụng phương pháp này hiển thị một mẫu thông thường và trong một số trường hợp, chẳng hạn như trong hình ảnh thứ 6 và thứ 11, có các pixel màu đỏ không cần thiết.

Các pixel màu đỏ bổ sung này có thể được loại bỏ dễ dàng bằng cách lặp lại trên hình ảnh và thực hiện các thao tác tương tự như được giải thích ở trên nhưng từ góc dưới bên phải đến góc trên cùng bên trái. Vượt qua thứ hai này nhanh hơn nhiều vì số lượng pixel màu đỏ phải được kiểm tra.

Các kết quả

Các hình ảnh được sửa đổi sau khi vượt qua lần thứ hai được liệt kê hai lần để hiển thị sự khác biệt.

18825

Số pixel màu đỏ: 18825

334

Số pixel màu đỏ: 334

1352

Số pixel màu đỏ: 1352

20214

Số pixel màu đỏ: 20214

nhập mô tả hình ảnh ở đây

Số pixel màu đỏ: 47268

63 nhập mô tả hình ảnh ở đây

Số pixel màu đỏ: 63 27

17889

Số pixel màu đỏ: 17889

259

Số pixel màu đỏ: 259

6746

Số pixel màu đỏ: 6746

586

Số pixel màu đỏ: 586

9 nhập mô tả hình ảnh ở đây

Số pixel màu đỏ: 9 1

126

Số pixel màu đỏ: 126

212

Số pixel màu đỏ: 212

683

Số pixel màu đỏ: 683

Tính toán điểm:

(1 + 6746) * (1 + 126) * (1 + 259) * (1 + 17889) * (1 + 334) * (1 + 586) * (1 + 18825) * (1 + 9) * (1 + +683) * (1 + 1352) * (1 + 20214) * (1 + 212) * (1 + 63) * (1 + 47268) = 1778700054505858720992088713763655500800000 ~ 1.7x10 ^ 42

Cập nhật tính toán điểm sau khi thêm lần thứ hai:

(1+ 18825) * (1+ 1352) * (1+ 20214) * (1+ 47268) * (1+ 27) * (1+ 17889) * (1+ 6746) * (1+ 586) * (1+ + 1) * (1+ 126) * (1+ 212) * (1+ 334) * (1 + 259) * (1 + 683) = 155636254769262638086807762454319856320000 ~ 1.5x10 ^ 41


Công việc tốt đẹp. Có vẻ như chúng ta có thể cần phải ghi điểm này theo ký hiệu khoa học: 1.7x10 ^ 42
BradC
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.