격차 해소


14

흰색 배경과 검은 점 세트가있는 흑백 이미지가 제공되면 흰색 픽셀 세트를 빨간색으로 페인트하여 각 검은 픽셀 쌍 사이에 경로가 있도록합니다.

세부

  • 경로는 연결된 픽셀 세트입니다 (8 개 인접 연결). 경로의 일부로 검은 색 픽셀을 사용할 수 있습니다. 목표는 위의 조건에서 빨간색 픽셀 세트를 최소화하고 해당 이미지를 출력하는 것입니다.

  • 최적의 솔루션 찾을 필요는 없습니다 .

  • 사소하고 동시에 최악의 해결책은 모든 흰색 픽셀을 빨간색으로 칠하는 것입니다.

  • 예 (픽셀은 가시성을 위해 확대됨) :

세부

  • 주어진 적절한 형식의 픽셀 이미지는 위에 지정된대로 연결된 점이있는 다른 이미지와 사용 된 빨간색 픽셀 수를 나타내는 정수를 반환합니다.
  • 점수는 14 개의 테스트 사례 각각에 대한 (1+ 빨간색 픽셀 수)의 곱입니다.
  • 목표는 가장 낮은 점수를 얻는 것입니다.

테스트 케이스

14 개의 테스트 케이스가 아래에 나와 있습니다. 출력의 연결성을 확인하는 파이썬 프로그램은 여기 에서 찾을 수 있습니다.

메타

@Veskah, @Fatalize, @ wizzwizz4 및 @trichoplax에게 다양한 제안을 해주셔서 감사합니다.


1
좋은 도전; 나는 독창적이고 다양한 점수 체계를 가진 사람들을 좋아합니다. 나는이 14 가지 특정 예뿐만 아니라 프로그램이 임의의 이미지에서 작동해야한다고 가정합니까? 그렇다면 Mona Lisa 이미지 당 512x512 또는 1024x1024와 같은 합리적인 최대 크기를 가정 할 수 있습니까?
BradC

피드백을 주셔서 감사합니다! 예, 14 가지 예를 모두 처리 할 수있는 한 최대 크기 (필요한 경우 최소 크기)를 가정 할 수 있습니다.
flawr

png를 ascii 또는 json 또는 구문 분석하기 쉬운 것으로 변환하는 방법은 무엇입니까?
ngn

자신의 점수를 계산할 수 있어야합니까? 빨간색을 칠하기 위해 흰색 픽셀의 가능한 모든 조합을 시도하고 모든 검은 색 픽셀을 연결하는 동안 가장 적은 빨간색 픽셀을 갖는 하위 집합을 확인하는 프로그램은 가능한 최고 점수를 얻지 만 너무 느려서 수명보다 오래 걸릴 것입니다 실제로 그 점수를 계산합니다.
레오 Tenenbaum

1
@ngn 김프에서 열고 netpbm 형식으로 저장합니다.
wizzwizz4

답변:


7

C, 점수 2.397x10 ^ 38

남자, 이것은 언어를 선택했기 때문에 너무 오래 걸렸습니다. 알고리즘이 상당히 일찍 작동했지만 메모리 할당에 많은 문제가 발생했습니다 (스택 오버플로로 인해 재귀 적으로 무료로 사용할 수 없으며 누출 크기가 컸습니다).

아직도! 모든 테스트 사례에서 다른 항목을 능가 하며 많은 시간 동안 최적의 솔루션 또는 매우 최적의 솔루션을 얻을 수도 있습니다 .

어쨌든, 여기 코드가 있습니다 :

#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;
}

테스트 대상 : Arch Linux, GCC 9.1.0, -O3

이 코드는 "cppm"이라고하는 사용자 지정 파일에서 입력 / 출력을 취합니다 (클래식 PPM 형식의 압축 버전과 같기 때문). 다음과 같이 변환하는 파이썬 스크립트는 다음과 같습니다.

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")

알고리즘 설명

이 알고리즘의 작동 방식은 이미지에서 빨간색 픽셀을 포함하여 연결된 모든 모양을 찾는 것으로 시작한다는 것입니다. 그런 다음 첫 번째 픽셀을 가져 와서 다른 모양을 만날 때까지 한 번에 한 픽셀 씩 더 확장합니다. 그런 다음 터치에서 원래 모양까지 모든 픽셀의 색상을 지정합니다 (연결된 경로를 따라 연결된 링크 목록 사용). 마지막으로, 하나의 모양 만 남을 때까지 생성 된 모든 새로운 모양을 찾아 프로세스를 반복합니다.

이미지 갤러리

테스트 케이스 1, 183 픽셀

테스트 사례 1

테스트 사례 2, 140 픽셀

테스트 사례 2

테스트 사례 3, 244 픽셀

테스트 사례 3

테스트 사례 4, 42 픽셀

테스트 케이스 4

테스트 케이스 5, 622 픽셀

테스트 케이스 5

테스트 케이스 6, 1 픽셀

테스트 케이스 6

테스트 사례 7, 104 픽셀

테스트 케이스 7

테스트 케이스 8, 2286 픽셀

테스트 케이스 8

테스트 케이스 9, 22 픽셀

테스트 사례 9

테스트 케이스 10, 31581 픽셀

테스트 사례 10

테스트 케이스 11, 21421 픽셀

테스트 케이스 11

테스트 케이스 12, 5465 픽셀

테스트 사례 12

테스트 케이스 13, 4679 픽셀

테스트 케이스 13

테스트 케이스 14, 7362 픽셀

테스트 케이스 14


2
잘 했어! 약간 더 최적의 솔루션으로 몇 가지 모양을 상상할 수는 있지만 매우 효율적으로 보입니다. 알고리즘을 통해 강제합니다.
BradC

6

파이썬, 2.62 * 10 ^ 40

이 알고리즘은 이미지의 검은 부분부터 시작하여 평면을 범람합니다 (BFS). 새 픽셀마다 플러딩 된 검은 부분을 기록합니다. 조상으로서 서로 다른 검은 부분을 가진 두 개의 인접 픽셀이 생기 자마자, 우리는 기본적으로이 두 개의 검은 부분을 방금 찾은 두 이웃의 조상을 통해 합쳐서 병합합니다. 이론적으로 이것은에서 구현 될 수 O(#pixels)있지만 코드의 양을 수용 가능한 수준으로 유지하려면이 구현이 약간 나쁩니다.

산출

여기에 이미지 설명을 입력하십시오 여기에 이미지 설명을 입력하십시오 여기에 이미지 설명을 입력하십시오 여기에 이미지 설명을 입력하십시오 여기에 이미지 설명을 입력하십시오 여기에 이미지 설명을 입력하십시오 여기에 이미지 설명을 입력하십시오 여기에 이미지 설명을 입력하십시오 여기에 이미지 설명을 입력하십시오 여기에 이미지 설명을 입력하십시오 여기에 이미지 설명을 입력하십시오 여기에 이미지 설명을 입력하십시오 여기에 이미지 설명을 입력하십시오 여기에 이미지 설명을 입력하십시오

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))

점수

(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

-이것이 최적이라고 생각합니다. 잘하셨습니다.-이건 최적 이 아닙니다 . 왜 그런지 이해가되지 않습니다.
wizzwizz4 4

@ wizzwizz4 사각형의 네 모퉁이의 쉬운 사례를 살펴보십시오. 최적의 솔루션은 X 일 것입니다. 이론적으로는 알고리즘에서이 솔루션을 찾을 수 있지만 그럴 가능성은 거의 없습니다. 각각 두 점을 연결하는 세 개의 경로가있는 솔루션을 찾을 가능성이 훨씬 큽니다.
flawr

@ wizzwizz4 그렇습니다. 위키 백과 텍스트 예제를 확대하면 다른 연결 경로가 빨간색 픽셀 또는 두 개를 저장 한 작은 장소가 많이 나타납니다. 그들은 합산됩니다.
BradC

그러나 이것은 페그의 비누 거품처럼 보이며 이는 Steiner 트리 문제에 대한 합법적 인 해결책 입니다.
wizzwizz4 4

1
@ wizzwizz4 그렇다면 차이점은 점을 연결하지 않고 세트 를 연결 한다는 것이므로 각 세트에서 어떤 점 을 최적의 방법으로 연결할지 결정해서는 안됩니다 . 텍스트 예제를 다시 확대하면 각 모양의 어떤 부분이 연결되어 있는지 와 관련하여 개선 부분이 대부분 입니다.
BradC

5

파이썬 3 : 1.7x10 ^ 42 1.5x10 ^ 41

사용 Pillow, numpyscipy.

이미지는 images스크립트와 동일한 디렉토리 에있는 폴더에있는 것으로 가정합니다 .

면책 조항 : 모든 이미지를 처리하는 데 시간이 오래 걸립니다.

암호

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()

설명

사소한 해결책. 이미지의 모든 흰색 픽셀의 색상을 빨간색으로 변경하여 시작합니다. 이렇게하면 모든 요소 (아일랜드의 검은 픽셀)가 연결됩니다.

그런 다음 왼쪽 상단에서 시작하여 오른쪽 및 아래쪽으로 이동하여 이미지의 모든 픽셀을 반복합니다. 모든 빨간 픽셀에 대해 색상을 흰색으로 바꿉니다. 이러한 색상 변경 후에도 여전히 하나의 요소 (현재 검은 색과 빨간색 픽셀의 섬) 만있는 경우 픽셀을 흰색으로두고 다음 픽셀로 이동합니다. 그러나 빨간색에서 흰색으로 색상이 변경된 후 요소 수가 1보다 크면 픽셀을 빨간색으로두고 다음 픽셀로 이동합니다.

최신 정보

알 수 있듯이이 방법을 사용하여 얻은 연결은 규칙적인 패턴을 나타내며 6 번째 및 11 번째 이미지와 같은 경우에는 불필요한 빨간색 픽셀이 있습니다.

이 여분의 적색 픽셀은 이미지를 다시 반복하고 위에서 설명한 것과 동일한 작업을 수행하지만 오른쪽 하단에서 왼쪽 상단으로 쉽게 제거 할 수 있습니다. 이 두 번째 패스는 확인해야하는 빨간색 픽셀의 양이 훨씬 빠릅니다.

결과

두 번째 패스 후에 수정 된 이미지는 차이점을 보여주기 위해 두 번 나열됩니다.

18825

빨간 픽셀 수 : 18825

334

빨간 픽셀 수 : 334

1352

빨간 픽셀 수 : 1352

20214

빨간 픽셀 수 : 20214

여기에 이미지 설명을 입력하십시오

빨간 픽셀 수 : 47268

63 여기에 이미지 설명을 입력하십시오

빨간 픽셀 수 : 63 27

17889

빨간 픽셀 수 : 17889

259

빨간 픽셀 수 : 259

6746

빨간 픽셀 수 : 6746

586

빨간 픽셀 수 : 586

9 여기에 이미지 설명을 입력하십시오

빨간 픽셀 수 : 9 1

126

빨간 픽셀 수 : 126

212

빨간 픽셀 수 : 212

683

빨간 픽셀 수 : 683

점수 계산 :

(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

두 번째 패스를 추가 한 후 점수 계산이 업데이트되었습니다.

(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


잘 했어. 과학적 표기법으로 점수를 매길 필요가있는 것 같습니다 : 1.7x10 ^ 42
BradC
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.