사과를 정렬하십시오!


11

문제

7 개의 버킷이 일렬로 정렬되어 있다고 상상해보십시오. 각 버킷에는 최대 2 개의 사과가 포함될 수 있습니다. 1에서 13까지 레이블이 지정된 13 개의 사과가 있습니다. 이들은 7 개의 버킷에 배포됩니다. 예를 들어

{5,4}, {8,10}, {2,9}, {13,3}, {11,7}, {6,0}, {12,1}

여기서 0은 빈 공간을 나타냅니다. 사과 표시 순서 내의 각 버킷에 관련되지 않은 경우 (예 : {5,4}에 상당하는 {4,5}).

대상 버킷에 다른 애플을위한 공간이 있으면 한 버킷에서 인접한 버킷으로 사과를 옮길 수 있습니다. 각 이동은 이동하려는 사과의 수로 표시됩니다 (빈 공간이 하나만 있기 때문에 분명합니다). 예를 들어, 이동을 적용

7

위의 배열에

{5,4}, {8,10}, {2,9}, {13,3}, {11,0}, {6,7}, {12,1}

객관적인

STDIN에서 배열을 읽고 다음 배열로 정렬하는 프로그램을 작성하십시오.

{1,2}, {3,4}, {5,6}, {7,8}, {9,10}, {11,12}, {13,0}

가능한 적은 움직임을 사용합니다. 다시 말하지만, 각 버킷 내에 사과가 나타나는 순서 는 관련이 없습니다. 버킷의 순서는 중요합니다. 쉼표로 구분 된 각 배열을 정렬하는 데 사용되는 동작을 출력해야합니다. 예를 들어

13, 7, 6, ...

당신의 점수는 다음 배열을 해결하는 데 필요한 이동 수의 합과 같습니다.

{8, 2}, {11, 13}, {3, 12}, {6, 10}, {4, 0}, {1, 7}, {9, 5}
{3, 1}, {6, 9}, {7, 8}, {2, 11}, {10, 5}, {13, 4}, {12, 0}
{0, 2}, {4, 13}, {1, 10}, {11, 6}, {7, 12}, {8, 5}, {9, 3}
{6, 9}, {2, 10}, {7, 4}, {1, 8}, {12, 0}, {5, 11}, {3, 13}
{4, 5}, {10, 3}, {6, 9}, {8, 13}, {0, 2}, {1, 7}, {12, 11}
{4, 2}, {10, 5}, {0, 7}, {9, 8}, {3, 13}, {1, 11}, {6, 12}
{9, 3}, {5, 4}, {0, 6}, {1, 7}, {12, 11}, {10, 2}, {8, 13}
{3, 4}, {10, 9}, {8, 12}, {2, 6}, {5, 1}, {11, 13}, {7, 0}
{10, 0}, {12, 2}, {3, 5}, {9, 11}, {1, 13}, {4, 8}, {7, 6}
{6, 1}, {3, 5}, {11, 12}, {2, 10}, {7, 4}, {13, 8}, {0, 9}

그렇습니다. 이러한 각 배열에는 해결책이 있습니다.

규칙

  • 솔루션은 이동 당 버킷 수로 다항식으로 실행해야합니다. 요점은 영리한 휴리스틱을 사용하는 것입니다.
  • 모든 알고리즘은 결정 론적이어야합니다.
  • 동점 일 경우 가장 짧은 바이트 수가 이깁니다.

2
사과를 옮길 수있는 공간이 하나 밖에 없을 때 목적지를 나타내는 요점은 무엇입니까?
John Dvorak

무차별 대입 솔루션이 적절한 시간 내에 실행되면 어떻게됩니까? 700M 상태 만 있으며 몇 분만에 쉽게 열거 할 수 있습니다. "합리적인 시간"을 정의하십시오.
John Dvorak

@JanDvorak Per "What 's point"-좋은 통화입니다. 그것은 나에게 일어나지 않았다. 나는 해결책을 무차별 대입하는 데 필요한 시간보다 적은 것으로 합리적으로 정의 하고있다.)
Orby

"합리적"이라는 정의가 먼저 무차별 대입 솔루션을 구현해야한다는 것을 의미합니까?
John Dvorak

버킷의 최종 주문이 중요합니까?
AMK

답변:


4

점수 : 448

내 생각은 1부터 시작하여 연속적으로 정렬하는 것입니다. 이렇게하면 공간을 이전 / 다음 바구니로 이동하려는 경우 두 사과 중 어떤 사과를 움직여야하는지 정확히 알 수 있습니다. 각각 1 분. 테스트 분석은 다음과 같습니다.

#1: 62     #6: 40
#2: 32     #7: 38
#3: 46     #8: 50
#4: 50     #9: 54
#5: 40    #10: 36

Total score: 448 moves

코드를 훨씬 더 많이 사용할 수 있지만 코드 품질이 좋을수록 추가 답변을 얻을 수 있습니다.

C ++ (501 바이트)

#include <cstdio>
#define S(a,b) a=a^b,b=a^b,a=a^b;
int n=14,a[14],i,j,c,g,p,q;
int l(int x){for(j=0;j<n;++j)if(a[j]==x)return j;}
int sw(int d){
    p=l(0);q=p+d;
    if(a[q]*d>a[q^1]*d)q^=1;
    printf("%d,", a[q]);
    S(a[q],a[p])
}
int main(){
    for(;j<n;scanf("%d", a+j),j++);
    for(;++i<n;){
        c=l(i)/2;g=(i-1)/2;
        if(c-g){
            while(l(0)/2+1<c)sw(2);
            while(l(0)/2>=c)sw(-2);
            while(l(i)/2>g){sw(2);if(l(i)/2>g){sw(-2);sw(-2);}}
        }
    }
}

추가 개선 사항은 C로 전환하고 큰 값부터 아래쪽으로 시작하여 점수를 낮추려고 시도 할 수 있습니다 (그리고 결국 두 솔루션을 결합).


1
코드의 하위 문자열은 이미 C 프로그램을 형성합니다. 특히 첫 번째 줄을 삭제하여 C에서 작동 할 수 있습니다.
feersum

@feersum 당신이 맞아요. 처음에는 더 많은 C ++ 관련 코드가 있었지만 그 후에 C 로의 전환을 염두에두고 제거 한 것 같습니다.
yasen

솔루션의 입력 형식을 변경하여 확인하려는 사람들에게 더 명확하게 변경하도록 지정할 수 있습니까?
Orby

2

C, 426 (448)

이것은 사과를 yasen의 방법 과 비슷한 1에서 13까지 한 번에 하나씩 정렬합니다 . 단, 더 큰 숫자를 올리거나 내릴 수있는 기회가있을 때마다 사과 가 걸립니다. 안타깝게도 이것은 첫 번째 테스트 문제의 성능 만 개선하지만 약간 개선되었습니다. 테스트 문제를 실행할 때 실수를했습니다. 내가 단순히 yasen의 방법을 다시 구현 한 것 같습니다.

#1: 62    #6: 40
#2: 32    #7: 38
#3: 46    #8: 50
#4: 50    #9: 54
#5: 40    #10: 36

중괄호 나 쉼표없이 입력해야합니다. 예 :

8 2 11 13 3 12 6 10 4 0 1 7 9 5

다음은 불필요한 줄 바꿈을 계산하는 423 바이트의 골프 코드입니다 (아마 더 골프를 칠 수는 있지만이 점수가 초과 될 것으로 예상합니다).

#define N 7
#define F(x,y) for(y=0;y<N*2;y++)if(A[y]==x)break;
#define S(x,y) x=x^y,y=x^y,x=x^y;
#define C(x,y) ((A[x*2]==y)||(A[x*2+1]==y))
A[N*2],i,j,d,t,b,a,n,s,v,u,w,g;main(){for(;i<N*2;i++)scanf("%d",A+i);g=1;while
(!v){F(0,i);b=i/2;F(g,u);w=u/2;d=b<w?1:-1;n=(b+d)*2;a=(b+d)*2+1;if(A[n]>A[a])
S(n,a);t=d-1?a:n;printf("%d,",A[t]);S(A[i],A[t]);while(C((g-1)/2,g))g++;v=1;for
(j=0;j<N*2;j++)if(!C(j/2,(j+1)%(N*2)))v=0;}}

그리고 ungolfed 코드는 점수를 인쇄합니다.

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

#define N 7

int apples[N*2];

int find(int apple)
{
    int i;
    for (i = 0; i < N*2; i++) {
        if (apples[i] == apple)
            return i;
    }    
}

void swap(int i, int j)
{
    int temp;
    temp = apples[i];
    apples[i] = apples[j];
    apples[j] = temp;
}

int contains(int bucket, int apple)
{
    if ((apples[bucket * 2] == apple) || (apples[bucket * 2 + 1] == apple))
        return 1;
    return 0;
}

int is_solved()
{
    int i, j;
    for (i = 0; i < N * 2; i++) {
        j = (i + 1) % (N * 2);
        if (!contains(i / 2, j))
            return 0;
    }
    return 1;
}

int main()
{
    int i, j, dir, bucket, max, min, score;
    int target_i, target_bucket, target;

    /* Read the arrangement */
    for (i = 0; i < N*2; i++) {
        scanf("%d ", apples + i);
    }

    target = 1;
    while (1) {

        i = find(0);
        bucket = i / 2;
        target_i = find(target);
        target_bucket = target_i / 2;

        /* Change the direction of the sort if neccesary */
        if (bucket < target_bucket) dir = 1;
        else dir = -1;

        /* Find the biggest and smallest apple in the next bucket */
        if (apples[(bucket + dir) * 2] < apples[(bucket + dir) * 2 + 1]) {
            min = (bucket + dir) * 2;
            max = (bucket + dir) * 2 + 1;
        } else {
            min = (bucket + dir) * 2 + 1;
            max = (bucket + dir) * 2;
        }

        /* If we're going right, move the smallest apple. Otherwise move the
           biggest apple */
        if (dir == 1) {
            printf("%d, ", apples[min]);
            swap(i, min);
            score++;
        } else {
            printf("%d, ", apples[max]);
            swap(i, max);
            score++;
        }

        /* Find the next apple to sort */
        while (contains((target - 1) / 2, target))
            target++;

        /* If we've solved it then quit */
        if (is_solved())
            break;
    }
    printf("\n");
    printf("%d\n", score);
}

2

파이썬 3-121

솔루션을 찾을 때까지 깊이를 증가시키면서 깊이 우선 검색을 구현합니다. 사전을 사용하여 방문 상태를 저장하므로 더 높은 깊이의 창이 없으면 다시 방문하지 않습니다. 확인할 상태를 결정할 때 잘못 배치 된 요소 수를 휴리스틱으로 사용하고 가능한 최상의 상태 만 방문합니다. 버킷 내 요소의 순서는 중요하지 않으므로 항상 버킷 내에서 순서를 유지합니다. 이를 통해 요소가 잘못 배치되었는지 쉽게 확인할 수 있습니다.

입력은 int의 배열이며 첫 번째 int는 버킷 수입니다.

예를 들어 # 8의 경우 (이 컴퓨터에서 실행하는 데 시간이 오래 걸리고 다른 컴퓨터는 몇 초 안에 완료됩니다) :

c:\python33\python.exe apples.py 7 3 4 10 9 8 12 2 6 5 1 11 13 7 0

테스트 세트에 대한 결과는 다음과 같습니다. # 1 : 12, # 2 : 12, # 3 : 12, # 4 : 12, # 5 : 11, # 6 : 11, # 7 : 10, # 8 : 14, # 9:13, # 10 : 14

코드는 다음과 같습니다.

import sys    

BUCKETS = int(sys.argv[1])    

# cleans a state up so it is in order
def compressState(someState):
  for i in range(BUCKETS):
    if(someState[2*i] > someState[2*i + 1]):
      temp = someState[2*i]
      someState[2*i] = someState[2*i + 1]
      someState[2*i + 1] = temp
  return someState    

state = compressState([int(x) for x in sys.argv[2:]])
print('Starting to solve', state)
WINNINGSTATE = [x for x in range(1, BUCKETS*2 - 1)]
WINNINGSTATE.append(0)
WINNINGSTATE.append(BUCKETS*2 - 1)
maxDepth = 1
winningMoves = []
triedStates = {}    

# does a depth-first search
def doSearch(curState, depthLimit):
  if(curState == WINNINGSTATE):
    return True
  if(depthLimit == 0):
    return False
  myMoves = getMoves(curState)
  statesToVisit = []
  for move in myMoves:
    newState = applyMove(curState, move)
    tns = tuple(newState)
    # do not visit a state again unless it is at a higher depth (more chances to win from it)
    if(not ((tns in triedStates) and (triedStates[tns] >= depthLimit))):
      triedStates[tns] = depthLimit
      statesToVisit.append((move, newState[:], stateScore(newState)))
  statesToVisit.sort(key=lambda stateAndScore: stateAndScore[2])
  for stv in statesToVisit:
    if(stv[2] > statesToVisit[0][2]):
      continue
    if(doSearch(stv[1], depthLimit - 1)):
      winningMoves.insert(0, stv[0])
      return True
  return False    

# gets the moves you can make from a given state
def getMoves(someState):
  # the only not-allowed moves involve the bucket with the 0
  allowedMoves = []
  for i in range(BUCKETS):
    if((someState[2*i] != 0) and (someState[2*i + 1] != 0)):
      allowedMoves.append(someState[2*i])
      allowedMoves.append(someState[2*i + 1])
  return allowedMoves    

# applies a move to a given state, returns a fresh copy of the new state
def applyMove(someState, aMove):
  newState = someState[:]
  for i in range(BUCKETS*2):
    if(newState[i] == 0):
      zIndex = i
    if(newState[i] == aMove):
      mIndex = i
  if(mIndex % 2 == 0):
    newState[mIndex] = 0
  else:
    newState[mIndex] = newState[mIndex-1]
    newState[mIndex-1] = 0
  newState[zIndex] = aMove
  if((zIndex % 2 == 0) and (newState[zIndex] > newState[zIndex+1])):
    newState[zIndex] = newState[zIndex+1]
    newState[zIndex+1] = aMove
  return newState    

# a heuristic for how far this state is from being sorted
def stateScore(someState):
  return sum([1 if someState[i] != WINNINGSTATE[i] else 0 for i in range(BUCKETS*2)])    

# go!
while(True):
  triedStates[tuple(state)] = maxDepth
  print('Trying depth', maxDepth)
  if(doSearch(state, maxDepth)):
    print('winning moves are: ', winningMoves)
    break
  maxDepth += 1

최적의 솔루션을 보는 것이 유용하기 때문에 이것을 상향 조정했지만 질문에 필요한 이동 당 버킷 수에서 다항식 시간에는 실행되지 않습니다. 최적의 솔루션을 생성하는 알고리즘 (일반적으로)이 다항식 시간으로 실행될 수 있다고 생각하지 않습니다.
Orby

첫 번째 테스트 문제의 경우 프로그램에서 10, 8, 1, 12, 6, 7, 11, 3, 5, 13, 4, 9를 생성하며 이는 유효한 솔루션이 아닙니다. 나는 당신이 그 질문을 오해했을 것입니다. "애플을 하나의 버킷에서 인접한 버킷으로 이동할 수 있습니다"(즉, 버킷을 임의의 버킷이 아닌) 오른쪽 또는 왼쪽으로 이동할 수 있다는 질문에 주목하십시오.
Orby

아, 나는 인접 제한을 완전히 놓쳤다! 내가 이것을 게시 한 후 나는 실행 시간 제한이 위반되었다는 잔소리를 의심했다. 반복되는 상태를 피하는 동적 프로그래밍 요소가 혼란 스러웠 기 때문에 작성했을 때 100 % 확신하지 못했습니다. 두 번의 시도로 실패하더라도 공감에 감사드립니다. 이것은 재미있는 퍼즐이며 더 나은 올바른 답변을 얻을 수 있는지 볼 것입니다.
RT
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.