Soyut bir oyun için en uygun strateji


12

Bir röportajda şu soruyu aldım (zaten çözemediğim, geçmişimi kandırmaya çalışmıyorum): Oyun pozitif bir tam sayı ile başlıyor . (Örn. ) Bu sayı, ikili gösterime dönüştürülür ve , ayarlanan bit sayısıdır . (Örn. , )A 0 = 1234 N 1 A 0 = b 100 1101 0010 N = 5.A0A0=1234N1A0=b100 1101 0010N=5.

Oyuncu 1 sayı seçer az daha . yalnızca 1 biti 1 olarak ayarlanmış olması gerekir. (Örn. ) . (Örn ). Bir hamle geçerli ise ayarlanan bit sayısı tatmin önceki kısıtlamaları ve varsa hala bir N eşit .A 0 B 0B0A0B0A 1 = A 0 - B 0 A 1 = 1234 - 512 = 722 = b 10 1101 0010 B 0 A 1B0=b10 0000 0000=512A1=A0B0A1=1234512=722=b1011010010B0A1

Oyuncu 2 den devam geçerli seçerek ardından 1 oyuncu devam eder benzeri, vb. Bir oyuncu geçerli hamlesi kalmazsa kaybeder.B 1 A 2A1B1A2

Her iki oyuncunun da en iyi şekilde oynadığını varsayarsak, kazanan oyuncuyu makul derecede verimli bir yöntem kullanarak belirleyin. (Sorun tanımımda, bunun üzerindeki kısıtlamalar, programın işaretli bir 32 bit tam sayıya uyan birkaç milyon giriş numarası için bir çözüm sunabilmesi gerektiğiydi.) Yani, çözümün tamamen analitik.


Buradaki kişisel ilgim, bana verilen 120 dakika içinde doğruluk hakkında hiçbir geri bildirim olmadan doğru çözümü bulup uygulamamın makul olup olmadığını anlamak; ya da bu "daha önce bu bulmacayı görüp görmedikleri" sorularından biri ise.

Başarısız olmalıydım çünkü makul bir strateji gibi görünen bir şeyi uygulamayı seçtim, bu da bana verilen birkaç test vakası için doğru sonuçları verdi, bu işi hızlı yapmak için çok fazla zaman harcadım ve yanlış teslim ettim tam çıkış zamanım bitti.

Geriye dönüp bakıldığında, küçük başlangıç ​​sayıları için kaba kuvvetli bir arama ve ezberlenmiş kısmi çözümler uygulamalıydım, ancak gezisi her zaman 20/20. Yine de beni flunkee olarak atlatan farklı bir ortak yaklaşım varsa merak ediyorum.


Açıklamadan, seçilen hareketlerin 1'e ayarlanmış tek bir bite sahip olması gerektiğini kavramadım (bunun sadece örneğin bir parçası olduğunu düşündüm).
jjmontes

@jjmontes - Bir B numarası seçmenin ilk kuralı olarak belirtilir - tüm örnekler bu şekilde belirtilir, parantez dışındaki her şey geneldir. Bunun nasıl daha net olabileceği konusunda bir öneriniz var mı?
18'de 40 milyon

1
Belki "Oyuncu 1 seçer bir sayı az daha , gereken 1'e sadece bir bit set var"? (belki de sadece bendim, ama bunun bir kısıtlama olduğunu anlamak için @orlp cevabını okumak zorunda kaldım). A 0B0A0
jjmontes

@Veedrac - Dostum, bilseydim, bitcount'u makul bir hızda çalıştırmak için harcadığım tüm çabam boşa gitmeyecekti. Neden çalıştığını açıklayan bir cevap mükemmel olurdu.
millimoose

@millimoose Bitcount, çoğu modern CPU için donanımda!
Veedrac

Yanıtlar:


21

Kendinizi sadece ikisinin bir güç substract edebilir ve popcount değiştiremezsiniz eğer çıkarmak olduğunu fark için biraz zaman ayırın diğer sayıdır bir konumda . Bunun sonucu her zaman bu konumda ve sayı başka hiçbir yerde değişmez.10 01011001

Diğer bir deyişle, oyun takas serisidir ve hepsi sağ tarafta ise oyun sona erer. Bu oyunun erken bitmesi imkansızdır - sıkışamazsınız. Her zaman tüm sıfırların solda ve hepsinin sağda olduğu bir konumda olacaksınız.1001

Bir oyunda tek belirleyici faktör, herkesin sağda olduğu duruma ulaşmak için kaç tane takas gerektiğidir ve kazanma veya kaybetme stratejisi yoktur. Takas sayısının paritesi tek belirleyici faktördür.

Peki kaç tane takas gerekiyor? saniyenin birbirini geçemeyeceğini unutmayın , bu yüzden onları numaralandırır ve takaslarla izlersek, son durumda aynı sırada kalırlar. Her takas onları son konumlarına bir adım daha yaklaştırır.1

Yani eğer inci (sağdan sayma, en sağdaki olduğu inci ) konumundayken, sağdan, ihtiyacı takasları doğru konuma ulaşmak için. Bu bize gereken takas miktarını hesaplamak için bir algoritma verir:1 1 0 1 k k - ii1101kki

i = 0
k = 0
total = 0
while n > 0:
    if n & 1:
       total += k - i
       i += 1
    n >>= 1
    k += 1

Şimdi totalkimin kazandığını görmek için paritesine bakabiliriz . Zaman karmaşıklığı .O(logn)


Bu doğru görünüyor, teslimden sonra bu yaklaşımın parçaları ve parçaları üzerinde tökezledim. Sanırım kodlamaya başlayarak silahı atlayarak ve sonuçta ortaya çıkan vahşi kaz kovalamacasında eşleştirildim.
17'de milimooz

Bunu düşünerek, stratejinin önemli olmadığı gerçeği, ya benim uygulamamda oldukça belirsiz bir hataya sahip olduğum anlamına gelir, ya da oyunu doğru oynayan diğer herhangi bir uygulama ile aynı sonuçları üretmiş olmalı ...
'13

5

Böyle bir sorunu çözmenin bir yolu şöyledir:

  • Önerdiğiniz "memoized brute-force" yaklaşımını kullanarak birkaç basit değerin çözümünü bulun.

  • Cevabı tahmin et (hangi pozisyonlar kazanıyor ve hangileri kaybediyor).

  • Cevabınızı kanıtlamaya çalışın. Eğer başarılı olursan, harika. Aksi takdirde, bir karşı örnek bulmaya çalışın ve başka bir cevap tahmin etmek için kullanın. Burada birkaç vakayı çözmek yararlı olabilir.

Ne kadar zaman aldığını söylemek gerçekten zor. Ancak, görüşmelerde mutlaka çözümü bulmanız beklenmez. Aksine, anketörler nasıl bilmek istiyorum yaklaştı problem çözme ve yapmayı başardı neyi ilerleme.


Evet hayır, beni reddettiler çünkü üretimim yanlıştı ve zamanım tükeniyordu.
17'de milimooz

Söz konusu kaba kuvvet yaklaşımı, strateji ile ilgili herhangi bir kısayol gerektirmediğinden doğru olurdu. Ancak, aynı zamanda - düşündüm - dayanılmaz derecede yavaş olurdu ve hatırlama aptalca bellek kullanmadan muhtemelen çok fazla yardım için çok fazla iş olabilirdi. Ya da belki değil, bunu daha sonra sadece sistemimden temizlemek için deneyeceğim.
milimooz

5

@ Orlp'nin cevabından, başlangıç ​​konumundan bitiş konumuna kadar olan deplasmanların toplamının paritesini istediğimizi unutmayın. Buna ek açıklama ekleyelim:

       9876543210
       9 76 4  1    (positions at start)
start: 1011010010
end:   0000011111
            43210   (positions at end)

Yani istiyoruz

  ((1 - 0) + (4 - 1) + (6 - 2) + (7 - 3) + (9 - 4)) & 1
= ((1 + 4 + 6 + 7 + 9) - (0 + 1 + 2 + 3 + 4)) & 1
= ((1 + 0 + 0 + 1 + 1) - (0 + 1 + 0 + 1 + 0)) & 1

İlk bölüm, tek pozisyonlardaki bit sayısının paritesidir. Maksimum işaretsiz tamsayıyı alarak, 0b11'e bölerek ve reddederek bunu maskeleyebilirsiniz.

= (bitcount(x & ~(UINT_MAX / 0b11)) ^ (0 + 1 + 0 + 1 + 0)) & 1

İkinci kısım, içindeki bit sayısının yarısının paritesidir x.

= (bitcount(x & ~(UINT_MAX / 0b11)) ^ (bitcount(x) >> 1)) & 1

bitcountdonanım popcntyönergesini kullanabilir veya bu tür hızlı indirimlerle yalnızca son veya ikinci-son bite ihtiyaç duyularak manuel olarak uygulanabilir .

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.