Küçük ve dengeli bir mobil cihaz oluşturun


18

Size bir sürü ağırlık verilir ve göreviniz bu ağırlıkları kullanarak küçük dengeli bir cep telefonu inşa etmektir.

Giriş, 1 ile 9 aralığındaki (tam dahil) bir tamsayı ağırlıklarının listesidir. Kopyalar olabilir.

Çıktı ascii bir cep telefonu, asılı olduğunda dengeleyecek bir resim. Belki de en iyi örnekle gösterilmiştir:

giriş

3 8 9 7 5

olası çıktı

         |
   +-----+---------+
   |               |
+--+-+        +----+------+
|    |        |           |
8   ++--+     7           5
    |   |
    9   3

Ascii karakterlerini gösterildiği gibi kullanmalısınız. Yatay ve dikey bölümler herhangi bir uzunlukta olabilir. Cep telefonunun hiçbir kısmı cep telefonunun bağlantısız başka bir kısmına dokunamaz (yatay veya dikey olarak). Tüm ağırlıklar en az 1 uzunluğunda dikey bir segmentten asılmalı ve tüm cep telefonunun asıldığı dikey bir segment olmalıdır.

Bir mobil büyüklüğü toplam sayısıdır +, -ve |karakterler bunu oluşturmak için gerekli. Düşük boyutlar daha iyidir.

Bir segmente istediğiniz sayıda bağlantı koyabilirsiniz. Örneğin:

giriş

2 3 3 5 3 9

olası çıktı

           |
   +---+---+-----------+
   |   |               |
+--+-+ 5               9
|  | |
2  | 3
   |
  +++
  | |
  3 3

Kazanan program, bir test seti girişi için en düşük mobil boyut ortalamasını oluşturabilen programdır. Gerçek test, sabit kodlamayı önlemek için süper sırlıdır, ancak böyle bir şey olacaktır:

8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 7
1 2 3 4 5 6 7 8 9 1 2 3 4 5 6 7 8 9 7 7
3 4 4 4 4 5 5 5 5 6 6 6 6 7 7 7 7

Fizik de dahil mi?
SİZ

1
@ S.Mark: Sanırım söyleyebilirdiniz. Fiziksel engelliler için, toplamı total_weight_hung_from_point * distance_of_point_from_pivotpivot noktasının her iki tarafında da aynı olmalıdır.
Keith Randall

Belki diyagramları incelemeyi kolaylaştırmak için, bir çubuğun yaklaşık iki tireye eşit olmasını sağlayın? Haliyle, diyagramlarınız dengenin dışında görünüyor.
Thomas O

Yanıtlar:


5

Python 2.

Ben ihanet ediyormuşum küçük bit:

  • Sadece bir yatay cep telefonu inşa ediyorum. Ben bir his var (ama bunu ispat değil) verilen şartlar altında en uygun cep aslında hep gelmez sadece yatay bir tane var. Düzenleme: Her zaman doğru değil; 2 2 9 1Nabb ile aşağıdaki yorumlarda bir karşı örnek buldu:

    Size 18:                Size 16:
       |                        |
    +-++--+-----+            +--++-+
    | |   |     |            |   | |
    2 9   2     1           -+-  9 1
                            | |
                            2 2
    
  • Sadece aptal kaba zorlama yapıyorum:

    1. Verilen ağırlıklar rastgele karıştırılır.
    2. Her seferinde iki ağırlık, dengeli kalacak şekilde en iyi konumlarda cep telefonuna yerleştirilir.
    3. Ortaya çıkan mobil, daha önce sahip olduğumuzdan daha iyiyse, unutmayın.
    4. Durulayın ve önceden belirlenmiş bir süre geçinceye kadar tekrarlayın.

Örnek girdileriniz için sonuçlarım; her biri 5 saniye boyunca çalıştırıldı (bunun küçük olanlar için saçma olduğunu biliyorum - sadece tüm olası permütasyonlardan geçmek daha hızlı olurdu). Rasgele bir öğe olduğundan, sonraki çalışmaların daha iyi veya daha kötü sonuçlar bulabileceğini unutmayın.

3 8 9 7 5
Tested 107887 mobiles, smallest size 20:
        |
+-+-----+-+--+
| |     | |  |
5 3     7 9  8

2 3 3 5 3 9
Tested 57915 mobiles, smallest size 23:
      |
+--+-++--+-+---+
|  | |   | |   |
3  5 9   3 3   2

8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 7
Tested 11992 mobiles, smallest size 50:
                |
+-+-+-+--+-+-+-+++-+-+--+-+-+-+-+
| | | |  | | | | | | |  | | | | |
8 8 8 8  8 8 8 8 8 8 8  7 8 8 8 8

1 2 3 4 5 6 7 8 9 1 2 3 4 5 6 7 8 9 7 7
Tested 11119 mobiles, smallest size 62:
                    |
+-+-+-+-+-+--+-+-+-+++-+-+-+--+-+-+-+-+-+
| | | | | |  | | | | | | | |  | | | | | |
2 7 5 6 6 8  3 2 3 7 9 7 8 1  1 7 9 5 4 4

3 4 4 4 4 5 5 5 5 6 6 6 6 7 7 7 7
Tested 16301 mobiles, smallest size 51:
                |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| | | | | | | | | | | | | | | | |
4 6 5 7 7 4 6 5 3 5 6 4 7 6 7 5 4

Kod (ayrıntılı, bu kod golf değil):

import time, random

def gcd(a, b):
    while b > 0:
        a, b = b, a % b
    return a

class Mobile(object):
    def __init__(self):
        self.contents = [None];
        self.pivot = 0;

    def addWeights(self, w1, w2):
        g = gcd(w1, w2)
        m1 = w2 / g
        m2 = w1 / g
        mul = 0
        p1 = -1
        while True:
            if p1 < 0:
                mul += 1
                p1 = mul * m1
                p2 = -mul * m2
            else:
                p1 *= -1
                p2 *= -1
            if self.free(p1) and self.free(p2):
                self.add(w1, p1)
                self.add(w2, p2)
                return

    def add(self, w, pos):
        listindex = self.pivot - pos 
        if listindex < 0:
            self.contents = [w] + (abs(listindex) - 1) * [None] + self.contents
            self.pivot += abs(listindex)
        elif listindex >= len(self.contents):
            self.contents += (listindex - len(self.contents)) * [None] + [w]
        else:
            self.contents[listindex] = w

    def at(self, pos):
        listindex = self.pivot - pos
        if 0 <= listindex < len(self.contents):
            return self.contents[listindex]
        return None

    def free(self, pos):
        return all(self.at(pos + d) is None for d in (-1, 0, 1))

    def score(self):
        return 1 + 2 * len(self.contents) - self.contents.count(None)

    def draw(self):
        print self.pivot * " " + "|"
        print "".join("+" if c is not None or i == self.pivot else "-" for i, c in enumerate(self.contents))
        print "".join("|" if c is not None else " " for c in self.contents)
        print "".join(str(c) if c is not None else " " for c in self.contents)

    def assertBalance(self):
        assert sum((i - self.pivot) * (c or 0) for i, c in enumerate(self.contents)) == 0


weights = map(int, raw_input().split())

best = None
count = 0

# change the 5 to the number of seconds that are acceptable
until = time.time() + 5

while time.time() < until:
    count += 1
    m = Mobile()

    # create a random permutation of the weights
    perm = list(weights)
    random.shuffle(perm)

    if len(perm) % 2:
        # uneven number of weights -- place one in the middle
        m.add(perm.pop(), 0)

    while perm:
        m.addWeights(perm.pop(), perm.pop())

    m.assertBalance() # just to prove the algorithm is correct :)
    s = m.score()
    if best is None or s < bestScore:
        best = m
        bestScore = s

print "Tested %d mobiles, smallest size %d:" % (count, best.score())
best.draw()

@ Nabb: 9'dan daha yüksek ağırlıklar mümkün değildir. Gelince 1 9 2 8Ürettiği 1-------8+-9--2; başımın üstünden daha iyi bir şey bulamam (ama buna güvenmem) - bir şeyin var mı?
balpha

1
@balpha: Boşver, daha önce yorum yaptığımda düz düşünmüyordu. Bir nedenden dolayı onları 1-9 ve 2-8 olarak yapıştırabileceğinizi düşündüm, ancak açıkçası bu çiftlerin kendileri dengelenmiyor!
Nabb

Tamam, işte birden çok katmanla daha iyi olabilecek bir tane var: 2 2 9 1Yani (2 + 2) * 3 = 16 boyutu için 18 yerine 2-9+--2----118 = bir eşik (belki 5 veya 6) ) bundan sonra tek bir yatay sıra her zaman en uygunudur.
Nabb

Nabb: Evet; bu gerçekten iyi bir karşı örnek.
balpha

@ Nabb, Dengeli tek bir çubuk 2-2-+9-1, 13 puan (4*2+2*2 = 9*1+1*3). Bu yüzden iyi bir karşı örnek olduğunu sanmıyorum.
Keith Randall

1

Bu eski bir soru, ama sadece en iyi sorular sekmesinde göründüğünü gördüm, işte benim (en uygun) çözümüm:

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

int main(int argc, const char *const *argv) {
    if(argc < 2) {
        fprintf(stderr,
            "Balances weights on a hanging mobile\n\n"
            "Usage: %s <weight1> [<weight2> [...]]\n",
            argv[0]
        );
        return 1;
    }
    int total = argc - 1;
    int values[total];
    int maxval = 0;
    for(int n = 0; n < total; ++ n) {
        char *check = NULL;
        long v = strtol(argv[n+1], &check, 10);
        if(v <= 0 || v > INT_MAX || *check != '\0') {
            fprintf(stderr,
                "Weight #%d (%s) is not an integer within (0 %d]\n",
                n + 1, argv[n+1], INT_MAX
            );
            return 1;
        }
        values[n] = (int) v;
        if(values[n] > maxval) {
            maxval = values[n];
        }
    }
    int maxwidth = (int) log10(maxval) + 1;
    for(int n = 0; n < total; ++ n) {
        int width = (int) log10(values[n]) + 1;
        fprintf(stdout,
            "%*s\n%*d\n",
            (maxwidth + 1) / 2, "|",
            (maxwidth + width) / 2, values[n]
        );
    }
    return 0;
}

Kurallara baktığımda, hile gibi olmadığından eminim. Bu, toplam 2 * number_of_inputs maliyeti için dikey bir zincirde verilen tüm sayıları çıkarır (bu mümkün olan en düşük değerdir, çünkü her sayı, düzen ne olursa olsun üzerinde bir çubuğa sahip olmalıdır). İşte bir örnek:

./mobile 3 8 9 7 5

üretir:

|
3
|
8
|
9
|
7
|
5

Tabii ki mükemmel dengede.


Başlangıçta bu meydan okuma ruhu içinde daha fazla bir şey deneyecektim, ancak hemen bu yapıya optimize edildiğini gördüm.


Muhtemelen açıklamamdan net değil, ancak |bir ağırlığın altına bir bağlayamazsınız .
Keith Randall

@KeithRandall ah ok; bunu göz önünde bulundurarak, bunu doğru bir şekilde çözmek zorunda kalabilirim.
Dave

1

İşte kaba en küçük tek sıralı çözümü zorlayan bir çözüm. Kod tüm permütasyonları tekrarlar ve her biri için kütle merkezini hesaplar. Kütle merkezinin tamsayı koordinatları varsa, bir çözüm bulduk.

Tüm permütasyonlar denendikten sonra, mevcut ağırlık grubumuzdaki karışıma (kütle 0 ağırlığına eşdeğer) bir segment ekliyoruz ve tekrar deniyoruz.

Programı çalıştırmak için python balance.py 1 2 2 4 .

#!/usr/bin/env python3
import itertools, sys

# taken from http://stackoverflow.com/a/30558049/436792
def unique_permutations(elements):
    if len(elements) == 1:
        yield (elements[0],)
    else:
        unique_elements = set(elements)
        for first_element in unique_elements:
            remaining_elements = list(elements)
            remaining_elements.remove(first_element)
            for sub_permutation in unique_permutations(remaining_elements):
                yield (first_element,) + sub_permutation

def print_solution(cm, values):
    print(('  ' * cm) + '|')
    print('-'.join(['-' if v == 0 else '+'  for v in values]))
    print(' '.join([' ' if v == 0 else '|'  for v in values]))
    print(' '.join([' ' if v == 0 else str(v) for v in values]))



input = list(map(int, sys.argv[1:]))
mass = sum(input)
while True:
    n = len(input)
    permutations = filter(lambda p: p[0] != 0 and p[n-1] != 0, unique_permutations(input))
    for p in permutations:
        cm = 0
        for i in range(n):
            cm += p[i] * i;
        if (cm % mass == 0):
            print_solution(cm//mass, p)
            sys.exit(0)
    input.append(0)

Bu en iyi çözümleri üretir:

    |
+-+-+-+-+
| | | | |
8 3 9 5 7


    |
+-+-+-+-+-+
| | | | | |
9 2 3 5 3 3

                |
+-+-+-+-+-+-+---+-+-+-+-+-+-+-+-+
| | | | | | |   | | | | | | | | |
8 8 8 8 8 8 8   8 8 8 8 8 8 8 8 7


                        |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| | | | | | | | | | | | | | | | | | | |
1 1 2 2 3 3 4 4 8 8 5 5 6 6 7 7 7 7 9 9


                  |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| | | | | | | | | | | | | | | | |
3 4 4 4 4 5 5 5 5 6 7 6 7 7 7 6 6

0

Python 3

Bu, herhangi bir test vakasında en iyisinden 1'den daha kötü bir şey değil, sanırım ve 5 saniye içinde yapıyor.

Temel olarak, tek çubuk yaklaşımı kullanıyorum. Girişi rastgele sıralıyorum, sonra ağırlıkları çubuğa birer birer yerleştiriyorum. Her eleman ya zamanın% 75'ini ve ikincisini% 25'ini kullanarak her iki taraftaki fazla ağırlığı en aza indirgeyen konuma veya bu perspektiften en iyi ikinci pozisyona yerleştirilir. Sonra, mobilin sonunda dengeli olup olmadığını ve şimdiye kadar bulunan en iyi mobilden daha iyi olup olmadığını kontrol ediyorum. En iyi olanı saklıyorum, sonra 5 saniye aramanın ardından durdurup yazdırıyorum.

Sonuçlar, 5 saniyelik çalışmalarda:

py mobile.py <<< '3 8 7 5 9'
Best mobile found, score 15:
    |    
+-+-+-+-+
| | | | |
8 7 3 5 9
py mobile.py <<< '2 2 1 9'
Best mobile found, score 13:
   |    
+-++-+-+
| |  | |
1 9  2 2
py mobile.py <<< '2 3 3 5 3 9'
Best mobile found, score 18:
      |    
+-+-+-+-+-+
| | | | | |
2 3 3 5 9 3
py mobile.py <<< '8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 7'
Best mobile found, score 49:
                |               
+-+--+-+-+-+-+-+++-+-+-+-+-+-+-+
| |  | | | | | | | | | | | | | |
7 8  8 8 8 8 8 8 8 8 8 8 8 8 8 8
\py mobile.py <<< '1 2 3 4 5 6 7 8 9 1 2 3 4 5 6 7 8 9 7 7'
Best mobile found, score 61:
                    |                   
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+--+
| | | | | | | | | | | | | | | | | | |  |
1 7 7 5 4 3 1 9 6 7 8 2 2 9 3 7 6 5 8  4
py mobile.py <<< '3 4 4 4 4 5 5 5 5 6 6 6 6 7 7 7 7'
Best mobile found, score 51:
                |                
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| | | | | | | | | | | | | | | | |
4 4 6 7 7 4 5 7 6 6 5 4 6 3 5 5 7

Kod:

import random
import time

class Mobile:
    def __init__(self):
        self.contents = {}
        self.lean = 0

    def usable(self, loc):
        return not any(loc + k in self.contents for k in (-1,0,1))
    def choose_point(self, w):
        def goodness(loc):
            return abs(self.lean + w * loc)
        gl = sorted(list(filter(self.usable,range(min(self.contents.keys() or [0]) - 5,max(self.contents.keys() or [0]) + 6))), key=goodness)
        return random.choice((gl[0], gl[0], gl[0], gl[1]))

    def add(self, w, loc):
        self.contents[loc] = w
        self.lean += w*loc

    def __repr__(self):
        width = range(min(self.contents.keys()), max(self.contents.keys()) + 1)
        return '\n'.join((''.join(' ' if loc else '|' for loc in width),
                          ''.join('+' if loc in self.contents or loc == 0 else '-' for loc in width),
                          ''.join('|' if loc in self.contents else ' ' for loc in width),
                          ''.join(str(self.contents.get(loc, ' ')) for loc in width)))

    def score(self):
        return max(self.contents.keys()) - min(self.contents.keys()) + len(self.contents) + 2

    def my_score(self):
        return max(self.contents.keys()) - min(self.contents.keys()) + 1

best = 1000000
best_mob = None
in_weights = list(map(int,input().split()))
time.clock()
while time.clock() < 5:
    mob = Mobile()
    for insert in random.sample(in_weights, len(in_weights)):
        mob.add(insert, mob.choose_point(insert))
    if not mob.lean:
        if mob.score() < best:
            best = mob.score()
            best_mob = mob

print("Best mobile found, score %d:" % best_mob.score())
print(best_mob)

Bence bu çözümlerin en düşük olduğuna inanıyorum, 10 dakikalık bir çalışmadan sonra bulduğum bu çözüme sahip olan en uzun çözümdür:

Best mobile found, score 60:
                   |                   
+-+-+-+-+-+-+-+-+-+++-+-+-+-+-+-+-+-+-+
| | | | | | | | | | | | | | | | | | | |
3 2 9 4 7 8 1 6 9 8 7 1 6 2 4 5 7 3 5 7
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.