Sắp xếp vài quả táo!


11

Vấn đề

Hãy tưởng tượng 7 thùng xếp thành một hàng. Mỗi thùng có thể chứa tối đa 2 quả táo. Có 13 quả táo được dán nhãn từ 1 đến 13. Chúng được phân phối trong số 7 thùng. Ví dụ,

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

Trong đó 0 đại diện cho không gian trống. Thứ tự mà táo xuất hiện trong mỗi thùng không liên quan (ví dụ: {5,4} tương đương với {4,5}).

Bạn có thể di chuyển bất kỳ quả táo nào từ một thùng sang một thùng liền kề, miễn là có chỗ trong thùng đích cho một quả táo khác. Mỗi di chuyển được mô tả bằng số táo bạn muốn di chuyển (không rõ ràng vì chỉ có một khoảng trống). Ví dụ: áp dụng di chuyển

7

để sắp xếp ở trên sẽ dẫn đến

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

Mục tiêu

Viết chương trình đọc một sắp xếp từ STDIN và sắp xếp nó vào sắp xếp sau

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

sử dụng càng ít di chuyển càng tốt. Một lần nữa, thứ tự mà táo xuất hiện trong mỗi thùng không liên quan. Thứ tự của các thùng không thành vấn đề. Nó sẽ xuất ra các di chuyển được sử dụng để sắp xếp từng sắp xếp được phân tách bằng dấu phẩy. Ví dụ,

13, 7, 6, ...

Điểm của bạn bằng tổng số lần di chuyển cần thiết để giải quyết các sắp xếp sau:

{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}

Vâng, mỗi cách sắp xếp này có một giải pháp.

Quy tắc

  • Giải pháp của bạn phải chạy trong thời gian đa thức về số lượng xô mỗi lần di chuyển. Vấn đề là sử dụng phương pháp phỏng đoán thông minh.
  • Tất cả các thuật toán phải được xác định.
  • Trong trường hợp hòa, số byte ngắn nhất sẽ thắng.

2
Điểm biểu thị điểm đến là gì khi chỉ có một không gian bạn có thể di chuyển một quả táo đến?
John Dvorak

Điều gì xảy ra nếu giải pháp vũ phu của tôi chạy trong một khoảng thời gian hợp lý? Chỉ có 700M tiểu bang - dễ dàng đếm được trong vài phút. Xác định "lượng thời gian hợp lý".
John Dvorak

@JanDvorak Per "What the point" - cuộc gọi tốt. Điều đó đã không xảy ra với tôi. Tôi đang xác định hợp lý ở đây là ít hơn lượng thời gian cần thiết để vũ phu giải pháp;)
Orby

Định nghĩa của bạn về "hợp lý" có nghĩa là trước tiên chúng ta nên thực hiện giải pháp vũ phu, sau đó bất cứ điều gì nhanh hơn?
John Dvorak

Là thứ tự cuối cùng của xô quan trọng?
AMK

Câu trả lời:


4

Điểm: 448

Ý tưởng của tôi là sắp xếp chúng liên tiếp, bắt đầu bằng 1. Điều này mang lại cho chúng ta một tài sản tuyệt vời mà khi chúng ta muốn di chuyển không gian sang giỏ trước / tiếp theo, chúng ta biết chính xác loại táo nào chúng ta phải di chuyển - tối đa / tối thiểu một, tương ứng. Đây là sự cố thử nghiệm:

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

Total score: 448 moves

Mã có thể được đánh gôn nhiều hơn, nhưng chất lượng mã tốt hơn sẽ thúc đẩy các câu trả lời bổ sung.

C ++ (501 byte)

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

Những cải tiến hơn nữa có thể là chuyển sang C và cố gắng hạ thấp điểm số bằng cách bắt đầu từ các giá trị lớn trở xuống (và cuối cùng chúng kết hợp cả hai giải pháp).


1
Một chuỗi con của mã của bạn đã tạo thành một chương trình C. Cụ thể, nó có thể hoạt động trong C bằng cách xóa dòng đầu tiên.
frageum

@feersum Bạn nói đúng. Lúc đầu, tôi có nhiều mã cụ thể hơn C ++, nhưng sau đó với việc chuyển sang C trong tâm trí, có vẻ như tôi đã thoát khỏi nó.
yasen

Bạn có thể chỉ định rằng bạn đã thay đổi định dạng đầu vào trong giải pháp của mình để làm cho nó rõ ràng hơn với những người cố gắng xác minh nó không?
Orby

2

C, 426 448

Cách này sắp xếp từng quả táo từ 1 đến 13 tương tự như phương pháp của yasen , ngoại trừ bất cứ khi nào nó có cơ hội di chuyển một số lớn hơn lên hoặc một số nhỏ hơn, nó sẽ lấy nó. Đáng buồn thay, điều này chỉ cải thiện hiệu suất trong vấn đề thử nghiệm đầu tiên, nhưng đó là một cải tiến nhỏ. Tôi đã phạm một lỗi khi chạy các vấn đề kiểm tra. Có vẻ như tôi chỉ đơn giản là thực hiện lại phương pháp của yasen.

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

Nó nhận đầu vào mà không cần dấu ngoặc hoặc dấu phẩy, ví dụ

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

Đây là mã được chơi ở mức 423 byte, tính một số dòng mới không cần thiết (có thể được đánh gôn nhiều hơn, nhưng tôi hy vọng điểm này sẽ bị đánh bại):

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

Và mã không được mã hóa, cũng in điểm:

#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

Trăn 3 - 121

Điều này thực hiện tìm kiếm theo chiều sâu với độ sâu tăng dần cho đến khi tìm thấy giải pháp. Nó sử dụng một từ điển để lưu trữ các trạng thái đã truy cập để nó không truy cập lại chúng trừ khi có cửa sổ có độ sâu cao hơn. Khi quyết định kiểm tra trạng thái nào, nó sử dụng số lượng phần tử bị đặt sai làm heuristic và chỉ truy cập các trạng thái tốt nhất có thể. Lưu ý rằng vì thứ tự của các phần tử trong nhóm của chúng không quan trọng, nên nó luôn duy trì thứ tự trong các thùng. Điều này giúp dễ dàng kiểm tra nếu một phần tử bị đặt sai vị trí.

Đầu vào là một mảng các số nguyên, với số đầu tiên là số nhóm.

Vì vậy, ví dụ, đối với # 8 (cái này mất rất nhiều thời gian để chạy trên máy của tôi, những cái khác kết thúc sau vài giây):

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

Dưới đây là kết quả trên bộ kiểm tra: # 1: 12, # 2: 12, # 3: 12, # 4: 12, # 5: 11, # 6: 11, # 7: 10, # 8: 14, # 9: 13, # 10: 14

Đây là mã:

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

Tôi đã ủng hộ điều này bởi vì rất hữu ích khi xem các giải pháp tối ưu, nhưng lưu ý rằng điều này không chạy trong thời gian đa thức về số lượng xô mỗi lần di chuyển theo yêu cầu của câu hỏi. Tôi không tin rằng bất kỳ thuật toán nào tạo ra một giải pháp tối ưu (nói chung) đều có thể chạy trong thời gian đa thức.
Orby

Đối với bài toán thử nghiệm đầu tiên, chương trình của bạn tạo ra 10, 8, 1, 12, 6, 7, 11, 3, 5, 13, 4, 9, đây không phải là một giải pháp hợp lệ. Tôi nghĩ rằng bạn có thể đã hiểu sai câu hỏi. Lưu ý câu hỏi nói rằng "Bạn có thể di chuyển bất kỳ quả táo nào từ một thùng sang một thùng liền kề" tức là xô sang phải hoặc trái của nó (không phải là một thùng tùy ý).
Orby

Oh, tôi hoàn toàn bỏ lỡ các hạn chế kề. Sau khi tôi đăng bài này, tôi đã có một sự nghi ngờ dai dẳng rằng việc hạn chế thời gian chạy cũng bị vi phạm. Tôi không chắc chắn 100% khi tôi viết nó, bởi vì yếu tố lập trình động để tránh các trạng thái lặp đi lặp lại làm tôi bối rối. Cảm ơn các upvote mặc dù điều này thất bại trong hai lần đếm; đây là một câu đố thú vị và tôi sẽ xem liệu tôi có thể đưa ra câu trả lời hợp lệ hơn không.
RT
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.