Google Code Jam Great Wall Sorunu için daha hızlı bir çözüm var mı


16

Aşağıdaki Google Code Jam 1.C yuvarlak sorusunu düşünün :

Çin Seddi, tüm konumlardaki yüksekliğin olduğu sonsuz bir çizgi olarak başlar .0

Bazı kabileler , , duvara aşağıdaki parametrelere göre saldırır - bir başlangıç ​​günü, , bir başlangıç ​​gücü , bir başlangıç ​​batı koordinatı, ve bir başlangıç ​​doğu koordinatı, . Bu ilk saldırı gününde , aralığında , kuvvetinde meydana gelir . içinde yüksekliğine sahip herhangi bir bölüm varsa , saldırı başarılı olur ve günün sonunda, duvar yüksekliği sonra yüksekte olurNN1000DSWED[W,E]S[W,E]<S[W,E]<SS (veya daha büyük, o gün başka bir saldırı gücüyle aynı segmente )S>S

Her kabile geri çekilmeden önce saldırıya kadar gerçekleştirecek ve her saldırı bir önceki saldırıdan itibaren tekrarlanarak belirlenecektir. Her kabilenin saldırı sırasını belirleyen , ve vardır: Saldırılar arasında gün bekleyecek , her saldırı için saldırı aralıklarını birimleri (negatif = batı, pozitif = doğu), ancak menzilin boyutu aynı kalacak ve güçleri de her saldırıdan sonra sabit bir değer artacak / azalacaktır.1000δDδXδSδD1δX

Sorunun amacı, saldıran kabilelerin tam bir açıklaması verildiğinde, saldırılarının kaçının başarılı olacağını belirlemektir.

Yaklaşık 20 saniye içinde çalışan bir çözümü kodlamayı başardım: Uyguladığım çözümün zamanını aldığını düşünüyorum , burada toplam saldırı sayısı bir simülasyon (maks ) ve saldırı menzillerindeki benzersiz kenar noktalarının toplam sayısı (maks. 2000000 ).A = 1000000 X =O(AlogA+(A+X)logX)A=1000000X=2000000

Yüksek seviyede benim çözümüm:

  • Tüm Kabile bilgilerini okur
  • Saldırı menzilleri için tüm benzersiz koordinatlarını hesaplar - O ( A )XO(A)
  • Duvarı, minimum yükseklik değerlerini izleyen aralıkları üzerinde tembel olarak güncellenen bir ikili ağaç olarak temsil eder . Bir yaprak, aralarında hiçbir şey olmayan iki X koordinatının açıklığıdır ve tüm ana düğümler, çocuklarının kapsadığı sürekli aralığı temsil eder. - O ( X log X )XXO(XlogX)
  • Her Kabilenin gerçekleştireceği tüm Saldırıları üretir ve güne göre sıralar - O(AlogA)
  • Her saldırı için, başarılı olup olmayacağına bakın ( sorgu süresi). Gün değiştiğinde, işlenmemiş tüm başarılı saldırılar arasında geçiş yapın ve duvarı buna göre güncelleyin ( her saldırı için X günlüğü güncelleme zamanı). - O ( A log X )logXgünlükXÖ(birgünlükX)

Sorum şu: dan daha iyi yapmanın bir yolu var mı ? Belki de, Kabilelerin ardışık saldırılarının doğrusal doğasından yararlanmanın stratejik bir yolu var mı? 20 saniye amaçlanan bir çözüm için çok uzun geliyor (Java bunun için suçlanıyor olsa da).Ö(birgünlükbir+(bir+X)günlükX)


3
Lütfen kapatma. Bu geçerli bir soru. Bir cevap, daha iyi yapamayacağımızı gösteren, gerçekten yapabileceğimiz en iyiyse, alt sınır kanıtı olacaktır. Örneğin, Element Ayrımcılık Sorunu'nu burada kullanabileceğimizi tahmin ediyorum, ancak düşünmek için zaman bulamadık.
Aryabhata

O zaman açık tutacağım :)
torquestomp

Yanıtlar:


2

Geliştirilmesi gereken açık alan bu adımdır:

Her Kabilenin gerçekleştireceği tüm Saldırıları üretir ve güne göre sıralar - O ( A log A Ö(birgünlükbir)

Kabilelerin belirli bir günden düzenli aralıklarla saldıracağını biliyoruz. Bu, aslında birçok önceden sıralanmış listeyi birleştirmemiz gerektiği anlamına gelir. Ayrıca sorun bildirimi bize hiçbir zaman 1000'den fazla kabilenin (yani birleştirilecek 1.000 liste) olmayacağını söyler; 1.000.000 maksimum saldırı ile karşılaştırıldığında küçük bir sayı! Uygulamanızın göreli zamanlamalarına bağlı olarak, bunun değiştirilmesi işlem süresini yarı yarıya azaltabilir.

Teorik karmaşıklığı optimize etmek için önerebileceğim tek şey bu, ancak bu değişiklikten sonra bunun optimal olacağına dair bir kanıtım yok.


Bulmacayı kendime verdim, ancak duvarın çok daha temsili bir gösterimini kullandım: std::mapduvarın yüksekliğinin değiştiği yerleri depolayan bir ikili arama ağacı ( kesin olarak C ++ 'lar ). Bununla birlikte, düğümleri gerektiği gibi ekleyip çıkarabildim (yani, karmaşık bir bölüm büyük, ezici bir saldırıya maruz kaldıysa veya aynı güce sahip birden fazla saldırıya maruz kaldıysa, düğüm sayısı önemli ölçüde azalır). Bu, büyük girişi 3,9 saniyede çözdü (orta özellikli geliştirme dizüstü bilgisayarımda). İyileştirmenin birkaç nedeni olduğundan şüpheleniyorum:

  • Belirttiğiniz gibi, boks ve kutudan çıkarma pahalı olabilir, ancak C ++ 'ın şablon tabanlı kapları bundan tamamen kaçınır.
  • Kullandığım duvar temsili teorik olarak daha kötü olsa da, vakaların büyük çoğunluğunda düğüm sayısını süper hızlı bir şekilde azaltabilmek (çoğu test vakası 1k düğümlerin altında maksimize edildi ve 2 dışında hepsi 10k'ın altındaydı) . Aslında, önemli bir zaman alan tek vaka # 7 idi, ki bu kesişen olmayan birçok aralığı test ediyor gibi görünüyor.
  • Ön işleme kullanmadım (aşamalar, her kabilenin ne zaman saldırı yapacağını takip ederek ve her turda en düşük eklemi arayarak belirlenir). Yine bu teorik olarak daha kötü, ancak vakaların çoğu için alt ek yükün daha hızlı olduğu konusunda şüpheleniyorum (bunu test edeceğim ve size geri döneceğim). Güncelleme : Yukarıda açıklanan yönteme benzer şekilde saldırılar için bir öncelik kuyruğu ekledim (büyük diziyi oluşturmak yerine onu anında hesapladığım halde) ve büyük girdi için sürenin 3,0 saniyeye düştüğünü gördüm.

Kısacası, algoritmanızın genel durumda neredeyse en uygun olduğunu düşünürken, tipik girişler için hızlandırmanın birkaç yolu vardır .


1

Aşağıdaki soru, bir cevap olduğu için sorudan çıkarılmıştır.

Diğer tartışmalara ve başarılı çözümlere bakmak, tarif ettiğim çözümün beklenen algoritma olduğunu gösteriyor. Çözümümdeki yavaşlama muhtemelen sadece otomatik boksun ve dizi tabanlı bir yapı yerine işaretçi tabanlı bir ağaç yapısının tembel kullanımından kaynaklanıyor - bu nedenle, bir çözüm varsa, muhtemelen bir bütün değildir. buradakinden çok daha iyi.

Çözüm burada bulunabilir . Burada gönderdiğimle aynı; bu yüzden daha verimli bir çözümün mevcut olmadığına inanmaya daha meyilliyim.

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.