Bomba düşürme algoritması


212

n x mNegatif olmayan tamsayılardan oluşan bir matrisim var . Örneğin:

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

"Bomba atmak", hedef hücrenin sayısını ve komşularının sekizini de en az sıfıra indirir.

x x x 
x X x
x x x

Tüm hücreleri sıfıra indirmek için gereken minimum bomba sayısını belirleyen bir algoritma nedir?

B Seçeneği (Dikkatli bir okuyucu olmamam nedeniyle)

Aslında sorunun ilk sürümü cevap aradığım sürüm değil. Tüm görevi dikkatlice okumadım, ek kısıtlamalar var, diyelim ki:

Sıradaki dizinin artmaması gerektiğinde basit sorun hakkında:

8 7 6 6 5 giriş sırası mümkündür

7 8 5 5 2 7 -> 8 art arda büyüdüğü için mümkün değildir.

Belki "daha kolay" vaka için cevap bulmak, daha zor bir çözüm bulmak için yardımcı olacaktır.

Not: Birkaç aynı duruma sahip olduğumuzda, üst çizgiyi temizlemek için minimum bomba gerektirdiğinde, sıranın "sol tarafında" en çok bomba kullanan birini seçtiğimize inanıyorum. Hala doğru olabilecek herhangi bir kanıt var mı?


4
Sadece bazı alanların örneğin 2 3 1 5 gibi atlanabileceğini görüyorum. küresel olarak nasıl çalışacağını bulun (eğer doğru yoldaysa). Temizleme 2, komşuların herhangi birine düşürülen 2 bomba kullanılmasını gerektirir ve 5, başka hasar setleri içerir. Ama sonra daha sonra ne yapacağımı bilmiyorum (yeniden yazdığınızda (azaldıktan sonra), o zaman iki seçeneğiniz var (bir uber hasar seti yok).
abc

23
Bu NP zor mudur? Maksimum Kapsam Sorununun bir varyantı gibi görünüyor .
Gizemli

14
Bana düşünecek ilginç bir şey verdiğiniz için +1
Nick Mitchinson

3
@Kostek, büyük sorun! Lütfen bağlantıyı gönderin.
Albay Panik

5
belki de açıklığa kavuşturmalısınız, şu soruyu söylediniz: what's the minimum amount of bombs required to clean the board?Bu, gerçek bir bombalama paterni bulmak zorunda olmanız değil, sadece minimum sayıda bomba bulunması gerektiği anlamına mı geliyor?
Yalan Ryan

Yanıtlar:


38

Bunu basit bir alt soruna indirmenin bir yolu var.

Açıklamanın, algoritmanın ve algoritmanın optimal bir çözüm sağlama nedeninin 2 bölümü vardır. Birincisi, ikincisi olmadan mantıklı olmaz, bu yüzden nedeniyle başlayacağım.

Dikdörtgeni bombalamayı düşünüyorsanız (büyük bir dikdörtgen varsayalım - henüz kenar durumu yok), çevre üzerindeki karelerin içi boş dikdörtgenini 0'a indirmenin tek yolunun çevreyi bombalamak veya içi boş dikdörtgeni bombalamak olduğunu görebilirsiniz. çevrenin içindeki kareler. Çevre katmanı 1'i ve içindeki dikdörtgen katman 2'yi arayacağım.

Önemli bir içgörü, nokta bombalama katmanı 1'in bulunmamasıdır, çünkü bunu yaparak elde ettiğiniz "patlama yarıçapı" her zaman katman 2'deki başka bir karenin patlama yarıçapında bulunur.

Böylece, çevreyi bombalamak için en uygun yolu bulma sorununu azaltabiliriz, o zaman tüm kareler 0 olana kadar tekrarlayabiliriz.

Ancak, elbette, çevreyi en uygun şekilde bombalamak mümkünse her zaman en uygun çözümü bulamaz, ancak X ekstra bomba kullanarak iç katmanı> X bombalarıyla daha basit hale getirme problemini basitleştirir. İzin verilen katmanı birinci olarak adlandırırsak, katman 2'de (katman 1'in içinde) fazladan bir X bombası koyarsak, katman 2'yi X'ten daha fazla bombalama çabasını azaltabilir miyiz? Başka bir deyişle, dış çevreyi azaltmada açgözlü olabileceğimizi kanıtlamamız gerekir.

Ama açgözlü olabileceğimizi biliyoruz. Katman 2'deki hiçbir bomba, katman 2'ye 0'da, katman 3'teki stratejik olarak yerleştirilmiş bir bombadan daha etkili olamayacağından ve daha önce olduğu gibi aynı sebepten dolayı - katman 3'e her kareyi etkileyecek olan her zaman bir bomba vardır. katman 2'ye yerleştirilen bir bombanın yapabileceği bir tabaka. Yani, açgözlü olmak bize asla zarar veremez (bu açgözlü anlamda).

Yani, tek yapmamız gereken, bir sonraki iç katmanı bombalayarak permitörü 0'a düşürmenin en uygun yolunu bulmaktır.

Köşeyi ilk önce 0'a bombalayarak asla incinmeyiz, çünkü sadece iç katmanın köşesi ona ulaşabilir, bu yüzden gerçekten seçeneğimiz yok (ve köşeye ulaşabilen herhangi bir bombanın içinde bir patlama yarıçapı var iç katmanın köşesinden patlama yarıçapı).

Bunu yaptıktan sonra, 0 köşesine bitişik çevre üzerindeki karelere sadece iç katmandan 2 kare ulaşılabilir:

0       A       B

C       X       Y

D       Z

Bu noktada çevre etkili bir şekilde kapalı 1 boyutlu bir döngüdür, çünkü herhangi bir bomba bitişik 3 kareyi azaltacaktır. Köşelerin yakınındaki bazı tuhaflıklar hariç - X, A, B, C ve D'yi "vurabilir".

Artık herhangi bir patlama yarıçapı hilesi kullanamayız - garip köşeler hariç her karenin durumu simetriktir ve hatta patlama yarıçapı bir diğerinin alt kümesidir. Bu, kapalı bir döngü yerine bir çizgi olsaydı (Albay Panik'in tartıştığı gibi) çözümün önemsiz olduğunu unutmayın. Uç noktalar 0'a düşürülmelidir ve yine uç noktalara bitişik noktaları bombalamanız asla zarar vermez, çünkü patlama yarıçapı bir süper settir. Uç noktanızı 0 yaptıktan sonra, hala yeni bir uç noktanız olur, bu yüzden tekrarlayın (satır tamamen 0 oluncaya kadar).

Dolayısıyla, katmandaki tek bir kareyi en iyi şekilde 0'a indirebilirsek, bir algoritmaya sahibiz (çünkü döngüyü kestik ve şimdi uç noktalarla düz bir çizgimiz var). En düşük değere sahip kareye bitişik bombalamanın (size 2 seçenek vererek), bu en düşük değerin 2 karesi içindeki en yüksek değerin mümkün olan en düşük olacağına inanıyorum (bunu yönetmek için bombalamayı bölmeniz gerekebilir) ama ben (henüz?) bir kanıtı yok.


+1 - Benzer bir şey yazacaktım. Sanırım sende var!
Rex Kerr

5
@ beher, lütfen sorunu dikkatlice okuyun. Bir kareyi bombalamak komşusunun sekizini de azaltır , bu yüzden orada olduğu varsayımı aslında doğrudur.
darksky

20
But, we do know we can be greedy...- Bunu satın almıyorum. Çevreyi düşünün 1 1 2 1 1 2. Minimum bomba sayısı 4'tür, ancak üç ayrı çözüm vardır. Her çözümün bir sonraki katman üzerinde farklı bir etkisi vardır. Sürece bir çevre için birden çok az çözümler vardır, sen olamaz tamamen iç katmanları dikkate alınmadan çevre izole eder. Bu sorunun geri izleme olmadan çözülebileceğini gerçekten sanmıyorum.
user1354557

4
Bu çözümü düşünüyordum, ama bu kadar basit görünüyor. Temizlemek için katman2'ye bomba bırakabileceğiniz doğrudur, katman1, ancak birden fazla çözüm varsa, daha yüksek katmanlar için çözümleri etkiler.
Luka Rahne

12
@psr: Bu çalışmıyor. Dış katman için en uygun olan bombalama yöntemi, küresel olarak optimal olmayabilir. Örnek: 0011100 0100010 0000000 0000000 1110111. İlk katmanı bombalamanın en iyi yolu, ikinci sıranın ortasında bombalamak ve dış katmanı öldürmek için toplam üç bomba almaktır. Ama sonra bir sonraki katmana bakmak için iki bombaya ihtiyacınız var. Optimal toplamda sadece dört bomba gerektirir: ilk iki sıra için iki, son sıra için iki bomba.
nneonneo

26

Pólya, "Bir sorunu çözemezseniz, çözebileceğiniz daha kolay bir sorun vardır: bulun."

Açık olan basit problem 1 boyutlu problemdir (ızgara tek bir satır olduğunda). En basit algoritma ile başlayalım - açgözlülükle en büyük hedefi bombalamak. Bu ne zaman yanlış gidiyor?

Verilen 1 1 1, açgözlü algoritma ilk önce hangi hücreyi bombaladığını kayıtsızdır. Tabii ki, merkez hücre daha iyidir - her üç hücreyi de aynı anda sıfırlar. Bu yeni bir algoritma A, "kalan toplamı en aza indirmek için bomba" olduğunu göstermektedir. Bu algoritma ne zaman yanlış gidiyor?

Verilen 1 1 2 1 1, algoritma bir 2., 3. veya 4. hücrelerini bombalama arasında kayıtsız kalır. Ancak 2. hücreyi terk etmek 0 0 1 1 1bombalamak 3. hücreyi terk etmek bombalamaktan daha iyidir 1 0 1 0 1. Bunu nasıl düzeltirim? 3. hücrenin bombalanmasındaki sorun, bizi solda çalışmamızı ve ayrı ayrı yapılması gereken sağda çalışmasını sağlamasıdır.

"Kalan toplamı en aza indirmek için bombalayın, ancak soldaki (bombaladığımız yerlerin) minimumunu ve sağdaki minimum değeri en üst düzeye çıkarın". Bu algoritmaya B deyin. Bu algoritma ne zaman yanlış olur?


Düzenleme: Yorumları okuduktan sonra çok daha ilginç bir sorunun tek boyutlu sorun değişti biter katılmak böylece olacağını kabul ediyorum. Bu konuda herhangi bir gelişme görmek isterim.


40
Bu cevabın neden bu kadar çok oy aldığından emin değilim - 1D davası neredeyse önemsiz, her zaman ilk pozitif elementin sağındaki elemanı bombalayın. Bu işe yarar çünkü solunda sadece 0 olan herhangi bir elemanı bombalamak için her zaman en uygun yol vardır. Köşe karelerini en iyi şekilde kaldırmak için bu 2D'ye genişletilebilir, ancak bunun ötesine uzanmanın açık bir yolunu görmüyorum ...?
BlueRaja - Danny Pflughoeft

3
@BlueRaja, diğer cevaplarda tartışılan açgözlü yaklaşımın yetersiz olduğunu açıkça gösterdi çünkü en azından ek bir kriterle desteklenmesi gerekiyordu. Bazı hedef seçimleri, toplam sayıda eşit bir azalmayla sonuçlansa bile, işleri diğerlerinden daha fazla yayılabilir. Bence bu 2D problem için faydalı bir fikir.
Tim Goodman

3
Ve genel olarak "2B kasaya takılı kalırsanız, önce 1D kasa deneyin" iyi bir tavsiye.
Tim Goodman

21
@Tim: "'ilk 1D davayı deneyin' iyi bir tavsiye" Evet, bu mükemmel bir yorum yapar; ama bu bir cevap değil ...
BlueRaja - Danny Pflughoeft

3
İyi bir noktaya sahip olduğunuzu düşünüyorum, ancak 1D kasası burada biraz yanıltıcı olabilir, çünkü kolayca daha yüksek boyutlara uzanmayan basit bir çözüme sahiptir. Periyodik sınır koşullarına sahip 1D vakasının (kasanın etrafındaki sargı) daha iyi olabileceğini düşünüyorum.
Tim Goodman

12

Zamanımın olmadığı için sadece kısmi bir çözümde durmak zorunda kaldım, ancak umarım bu kısmi çözüm bile bu sorunun çözülmesine yönelik potansiyel bir yaklaşım hakkında bazı bilgiler verir.

Zor bir sorunla karşılaştığımda, sorun alanı hakkında bir sezgi geliştirmek için daha basit sorunlar bulmayı seviyorum. Burada attığım ilk adım, bu 2-B problemini 1-B problemine indirgemekti. Bir satır düşünün:

0 4 2 1 3 0 1

Bir şekilde ya da böyle, sen gibi yaklaşık bombalamak gerekir biliyorum 4bombalama için hiçbir faydası yoktur, bir alt sayıdır nokta ayrıldığından beri 0'a aşağı almak için 4 kez yerinde 0veya 4bombalama üzerinde 2. Aslında, nokta 0'a 2inene kadar bombalamanın 4en azından bunu elde etmek için herhangi bir diğer strateji kadar iyi olduğuna inanıyorum (ancak titiz bir kanıt yok)4 bunu 0'a düşürmek için Bir stratejide soldan sağa doğru ilerleyebilir. bunun gibi:

index = 1
while index < line_length
  while number_at_index(index - 1) > 0
    bomb(index)
  end
  index++
end
# take care of the end of the line
while number_at_index(index - 1) > 0
  bomb(index - 1)
end

Birkaç bombalama emri örneği:

0 4[2]1 3 0 1
0 3[1]0 3 0 1
0 2[0]0 3 0 1
0 1[0]0 3 0 1
0 0 0 0 3[0]1
0 0 0 0 2[0]0
0 0 0 0 1[0]0
0 0 0 0 0 0 0

4[2]1 3 2 1 5
3[1]0 3 2 1 5
2[0]0 3 2 1 5
1[0]0 3 2 1 5
0 0 0 3[2]1 5
0 0 0 2[1]0 5
0 0 0 1[0]0 5
0 0 0 0 0 0[5]
0 0 0 0 0 0[4]
0 0 0 0 0 0[3]
0 0 0 0 0 0[2]
0 0 0 0 0 0[1]
0 0 0 0 0 0 0

Bir şekilde aşağıya inmesi gereken bir sayı ile başlama fikri caziptir, çünkü bir anda en azından diğer tüm çözümler kadar iyi olduğunu iddia eden bir çözüm bulmak mümkün olur .

En az olduğu kadar iyi olan bu aramanın hala mümkün olduğu karmaşıklıkta bir sonraki adım , tahtanın kenarındadır. Dış kenarı bombalamak için hiçbir zaman kesin bir fayda olmadığı açıktır; bir tanesini bombalamaktan ve diğer üç alanı ücretsiz olarak almaktan daha iyidir. Bu göz önüne alındığında, halkanın bir kenarın içine bombalanmasının en azından kenarı bombalamak kadar iyi olduğunu söyleyebiliriz . Dahası, bunu kenarın içinde doğru olanı bombalamanın aslında 0'a kadar kenar boşluklarını almanın tek yolu olduğu sezgisi ile birleştirebiliriz. Dahası, en uygun stratejiyi ( en az diğer stratejiler kadar iyidir) köşe sayılarını 0'a düşürmek için.

Köşe parçaları hakkındaki gözlem göz önüne alındığında, herhangi bir başlangıç ​​tahtasından tüm köşelerde sıfırlı bir tahtaya geçmek için en uygun stratejiyi bildiğimizi kesin olarak söyleyebiliriz. Bu böyle bir tahtanın bir örneğidir (yukarıdaki iki doğrusal tahtanın numaralarını ödünç aldım). Bazı alanları farklı şekilde etiketledim ve nedenini açıklayacağım.

0 4 2 1 3 0 1 0
4 x x x x x x 4
2 y y y y y y 2
1 y y y y y y 1
3 y y y y y y 3
2 y y y y y y 2
1 y y y y y y 1
5 y y y y y y 5
0 4 2 1 3 0 1 0

Bir üst satırdaki göreceksiniz gerçekten yakından daha önce gördüğümüz doğrusal örnek benzer. Daha önceki gözlemimizi hatırlatarak, en üst sırayı 0'a indirmenin en iyi yolu ikinci sırayı ( xsıra) bombalamaktır . Üst sırayı herhangi bir sırayı bombalayarak temizlemenin bir yolu yoktur yve sıradaki boşluğu bombalamak için üst sırayı bombalamanın ek bir yararı yoktur x.

Biz olabilir (karşılık gelen boşluk bombalama yukarıdan doğrusal stratejiyi uygulamak xkendimizi ilgilendiren, sıranın) sadece üst satıra ve başka bir şey. Böyle bir şeye giderdi:

0 4 2 1 3 0 1 0
4 x[x]x x x x 4
2 y y y y y y 2
1 y y y y y y 1
3 y y y y y y 3
2 y y y y y y 2
1 y y y y y y 1
5 y y y y y y 5
0 4 2 1 3 0 1 0

0 3 1 0 3 0 1 0
4 x[x]x x x x 4
2 y y y y y y 2
1 y y y y y y 1
3 y y y y y y 3
2 y y y y y y 2
1 y y y y y y 1
5 y y y y y y 5
0 4 2 1 3 0 1 0

0 2 0 0 3 0 1 0
4 x[x]x x x x 4
2 y y y y y y 2
1 y y y y y y 1
3 y y y y y y 3
2 y y y y y y 2
1 y y y y y y 1
5 y y y y y y 5
0 4 2 1 3 0 1 0

0 1 0 0 3 0 1 0
4 x[x]x x x x 4
2 y y y y y y 2
1 y y y y y y 1
3 y y y y y y 3
2 y y y y y y 2
1 y y y y y y 1
5 y y y y y y 5
0 4 2 1 3 0 1 0

0 0 0 0 3 0 1 0
4 x x x x x x 4
2 y y y y y y 2
1 y y y y y y 1
3 y y y y y y 3
2 y y y y y y 2
1 y y y y y y 1
5 y y y y y y 5
0 4 2 1 3 0 1 0

Bu yaklaşımdaki kusur son iki bombalamada çok belirginleşiyor. 4İkinci sıradaki ilk sütundaki rakamı azaltan tek bomba alanlarının birinci xve y. Son iki bombalama x, tam olarak aynı şeyi yapacak olan ilk bombalamadan açıkça daha düşüktür (üst sıradaki ilk noktaya göre, başka bir temizleme yolumuz yoktur). Mevcut stratejimizin yetersiz olduğunu gösterdiğimizden, stratejide bir değişikliğe açıkça ihtiyaç vardır.

Bu noktada, karmaşıklıkta bir adım geri gidebilir ve sadece bir köşeye odaklanabilirim. Bunu düşünelim:

0 4 2 1
4 x y a
2 z . .
1 b . .

Bu boşlukların almak için tek yolu temizlemek olduğu 4bazı kombinasyonlarını bomba sıfır aşağı x, yve z. Aklımda bazı akrobasi ile, emin optimal çözüm bomba oldukça değilim xardından üç kez ve adaha sonra b. Şimdi bu çözüme nasıl ulaştığımı çözmemiz gerekiyor ve eğer herhangi bir sezgi ortaya çıkarsa, bu yerel sorunu çözmek için bile kullanabiliriz. Bombalama yve zboşluk olmadığını fark ettim . Bu alanları bombalamanın mantıklı olduğu bir köşe bulmaya çalışmak, şuna benzer bir köşe verir:

0 4 2 5 0
4 x y a .
2 z . . .
5 b . . .
0 . . . .

Bunun için, en uygun çözümün y5 kez ve z5 kez bombalamak olduğu açıktır . Bir adım daha ileri gidelim.

0 4 2 5 6 0 0
4 x y a . . .
2 z . . . . .
5 b . . . . .
6 . . . . . .
0 . . . . . .
0 . . . . . .

Burada, optimal çözüm bomba olduğunu benzer sezgisel hissediyor ave b6 kez ve sonra x4 kez.

Şimdi bu sezgileri nasıl inşa edebileceğimiz prensiplere dönüştüreceğimiz oyununa dönüşüyor.

Umarım devam edecek!


10

Güncellenmiş soru için basit bir açgözlü algoritma en iyi sonucu verir.

A [0,0] bombalarını A hücresine [1,1] bırakın, ardından A [1,0] bombalarını A hücresine [2,1] düşürün ve bu işleme aşağı doğru devam edin. Sol alt köşeyi temizlemek için A hücresini [N-2,1] maksimum (A [N-1,0], A [N-2,0], A [N-3,0]) bombaları bırakın. Bu, ilk 3 sütunu tamamen temizleyecektir.

Aynı yaklaşımla sütunları 3,4,5, sonra sütunları 6,7,8 vb. Temizleyin.

Ne yazık ki bu orijinal soruna çözüm bulmaya yardımcı olmaz.


"Daha büyük" sorunun ("artmayan" kısıtlama olmadan) NP-zor olduğu kanıtlanabilir. İşte bir kanıt taslağı.

3 dereceye kadar düzlemsel bir grafiğe sahip olduğumuzu varsayalım . Bu grafik için minimum köşe örtüsü bulalım . Wikipedia makalesine göre, bu problem 3 dereceye kadar düzlemsel grafikler için NP-zordur. Ve Düzlemsel 3SAT'ın sertliği - 3SAT'tan indirilerek. Bu kanıtların her ikisi de "Algoritmik Alt Sınırlar" da son derslerde sunulmuştur. prof. Erik Demaine (ders 7 ve 9).

Orijinal grafiğin bazı kenarlarını (diyagramdaki sol grafik), her biri eşit sayıda ek düğümle böldüğümüzde, sonuçtaki grafik (diyagramdaki sağ grafik) orijinal köşeler için tamamen aynı minimum tepe kapağına sahip olmalıdır. Böyle bir dönüşüm, grafik köşelerini ızgaradaki rastgele konumlara hizalamaya izin verir.

resim açıklamasını buraya girin

Grafik köşelerini yalnızca çift sıralara ve sütunlara (bir tepe noktasında iki kenarın hiçbiri dar bir açı oluşturmayacak şekilde) yerleştirirsek, kenar olan her yere "olanları" ekleyin ve diğer ızgara konumlarına "sıfırlar" ekleyin, minimum tepe kapağını bulmak için orijinal sorun için herhangi bir çözümü kullanabiliriz.


Soldaki grafik nereden geliyor? Üzgünüm, açıklamanızı tam olarak anlamıyorum!
ryyst

1
@ryyst: soldaki grafik sadece düzlemsel grafiğin bir örneğidir. 4 dereceye kadar herhangi bir düzlemsel grafiğin ızgaraya hizalanmış grafiğe ve daha sonra n * m matrisine nasıl dönüştürüleceğini göstermek için kullanılır. Bu matrise uygulanan bir "bomba düşürme" algoritması, bu dönüştürülmüş grafik ve dolayısıyla bu "sol" grafik için tepe örtüsü problemini çözecektir.
Evgeny Kluev

Ah, şimdi anladım ve dönüşümünüzün doğru olduğuna inanıyorum. Teşekkürler!
ryyst

@EvgenyKluev, sanırım şimdi köşe kapağının hala "4 dereceye kadar düzlemsel grafikler" için NP-zor olduğunu kanıtlamanız gerekiyor.
Shahbaz

@Shahbaz: Bu kanıtın çok uzun olacağından korkuyorum. Bu yüzden ispat için bağlantı ekledim.
Evgeny Kluev

9

Bu sorunu tamsayı programlama sorunu olarak gösterebilirsiniz . (bu, bu soruna yaklaşmak için olası çözümlerden sadece biridir)

Puanınız:

a b c d
e f g h
i j k l
m n o p

f noktası için mesela 16 denklem yazılabilir

f <= ai + bi + ci + ei + fi + gi + ii + ji + ki   

tüm indekslerin toplamı ve tamsayı çözümü üzerinde minimuma indirilmiştir.

Çözüm elbette bu endekslerin toplamıdır.

Bu, 0 sınırındaki tüm xi ayarlanarak daha da basitleştirilebilir, böylece bu örnekte 4 + 1 denklemi elde edersiniz.

Sorun şu ki, bu tür problemleri çözmek için önemsiz bir algoritma yoktur. Ben bu konuda uzman değilim, ama lineer programlama olarak bu sorunu çözmek NP zor.


8

1
Katılıyorum. Ayrıca, çözümün ne olduğunu bilmek için yapılması gereken kesin hareketleri bilmek de gerekli değildir.
Luka Rahne

1
Sınırı 0 olarak ayarladığınızda, eşitsizliklerin sayısı hala 16'dır.
darksky

9

Bu kısmi bir cevap, olası sayıda bomba olabilecek bir alt sınır ve üst sınır bulmaya çalışıyorum.

3x3 ve daha küçük tahtalarda, çözüm önemsiz bir şekilde her zaman en büyük numaralı hücredir.

4x4'ten daha büyük levhalarda, ilk belirgin alt sınır köşelerin toplamıdır:

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

ancak bombayı düzenlerseniz, bu 4x4 tahtayı 2 + 1 + 6 + 4 = 13 bombadan daha az ile temizlemek imkansızdır.

Diğer cevaplarda, köşeyi ortadan kaldırmak için bombayı ikinci köşeye yerleştirmenin, bombayı köşeye yerleştirmekten asla daha kötü olmadığı belirtildi:

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

Yeni bir tahta vermek için ikinci köşeden bombalar yerleştirerek köşeleri sıfırlayabiliriz:

 0  1  1  6  0
 0  3  0  5  1
 2  1  1  1  0
 2  1  2  4  1
 0  0  0  0  0
 0  0  0  0  0
 0  3  0  2  0

Çok uzak çok iyi. Köşeleri temizlemek için 13 bombaya ihtiyacımız var.

Şimdi aşağıda işaretlenen 6, 4, 3 ve 2 sayısını inceleyin:

 0  1  1 *6* 0
 0  3  0  5  1
 2  1  1  1  0
*2* 1  2 *4* 1
 0  0  0  0  0
 0  0  0  0  0
 0 *3* 0  2  0

İkisini bombalamanın bir yolu yokBu hücrelerin tek bir bomba kullanarak , bu yüzden minimum bomba 6 + 4 + 3 + 2 arttı, bu yüzden köşeleri temizlemek için kullandığımız bomba sayısına ek olarak, Bu harita için gerekli bomba sayısı 28 bomba oldu. Bu haritayı 28'den az bomba ile temizlemek imkansız, bu harita için alt sınır bu.

Bir üst sınır oluşturmak için açgözlü algoritma kullanabilirsiniz. Diğer cevaplar, açgözlü bir algoritmanın 28 bomba kullanan bir çözüm ürettiğini göstermiştir. Daha önce hiçbir optimum çözümün 28'den az bombaya sahip olamayacağını kanıtladığımız için, 28 bomba gerçekten de en uygun çözümdür.

Açgözlü ve yukarıda bahsettiğim minimal sınır bulmak için yöntem olsa yakınsak değil, sanırım tüm kombinasyonları kontrol etmek için geri dönmek zorunda.

Alt sınırı bulma algoritması şöyledir:

  1. En yüksek numaralı bir öğe seçin, P olarak adlandırın.
  2. P ve P'den iki adım uzakta tüm hücreleri seçilemez olarak işaretleyin.
  3. minimumsListeye P ekleyin .
  4. Tüm hücreler seçilinceye kadar 1. adıma tekrarlayın.
  5. minimumsAlt sınırı almak için listeyi toplayın.

9

Bu açgözlü bir yaklaşım olacaktır:

  1. N (m) sırasının bir "skor" matrisini hesaplayın; burada (i, [j] pozisyonu (i, j) bombalanırsa matristeki noktaların toplam çıkarımdır. (Bir puanın maksimum puanı 9 ve minimum puanı 0'dır)

  2. Akıllıca satır hareket, en yüksek puanı (diyelim (i, j)) bulmak ve seçmek.

  3. Bomba (i, j). Bomba sayısını artırın.

  4. Orijinal matrisin tüm elemanları sıfır değilse, o zaman 1'e gidin.

Bunun en uygun çözüm olduğuna dair şüphelerim var.

Düzenle:

Yukarıda gönderdiğim Açgözlü yaklaşım, çalışırken, büyük olasılıkla bize en uygun çözümü vermiyor. Bu yüzden ona DP'nin bazı unsurlarını eklememiz gerektiğini düşündüm.

Herhangi bir zamanda, en yüksek "skor" a sahip pozisyonlardan birinin (skor [i] [j] = (i, j) bombalanırsa toplam puan indirimi) hedeflenmesi gerektiğine katılabiliriz. Bu varsayımdan başlayarak, yeni yaklaşım şöyledir:

NumOfBombs (M): (gerekli minimum bombalama sayısını döndürür)

  1. N X m düzeninde bir Matrix M verildi. M'nin tüm elemanları sıfırsa, 0 değerini döndürün.

  2. "Skor" matrisi M'yi hesaplayın.

    K'nin P1, P2, ... Pk (1 <= k <= n * m) farklı konumları olsun, M'deki en yüksek puanlara sahip konumlar olsun.

  3. dönüş (1 + dak. (NumOfBombs (M1), NumOfBombs (M2), ..., NumOfBombs (Mk))))

    burada M1, M2, ..., Mk, sırasıyla P1, P2, ..., Pk konumlarını bombalarsak ortaya çıkan matrislerdir.

Ayrıca, pozisyonların buna ek olarak nuke yapmasını istiyorsak, "min" sonuçlarını takip etmeliyiz.


3
Puanın mevcut değerlerin toplamı olarak ayarlanmasının daha iyi sonuçlar üretip getirmeyeceğini merak ediyorum. Bu, zemini daha verimli bir şekilde düzleştirecektir.
Eugene

@Eugene: Çok ilginç bir nokta. Yolunuzun daha iyi sonuçlar vermemesi için bir neden düşünemiyorum ...
SidR

@Eugene: Belki de çevredeki mevcut değerlerin toplamı "öncelik" ölçüsü için kullanılabilir? En yüksek puanı ve en yüksek önceliği olan düğümü
nuke

Bu cevabı okudum, sanırım yeni gönderdiğim ikinci cevaba benziyor (belki cevabımda biraz daha fazla dile getirilmişti). Her zaman maksimum puanla tek bir alan olmasının en uygun olacağını düşünüyorum , çünkü her bombalamanın mümkün olan en büyük etkiye sahip olacağı garanti edilecektir. Sipariş bombalama böylece optimum olmalıdır her adımda iyi biriyle gittiğini, önemli değildir. Ancak "en iyi" için bağlar olabileceğinden, belki de en iyi çözüm için, bir kravat olduğunda her ikisini de izlemeniz ve denemeniz gerekir.
Tim Goodman

1
@Eugene, belki seni takip etmiyorum. En büyük azaltma ile kalan tüm değerlerin en küçük toplamı arasındaki fark nedir? Kalan değerlerin toplamı (bombalamadan sonra) sadece mevcut toplam değer eksi o alanı bombalamaktan kaynaklanan azalmadır, yani bu eşdeğer değil mi?
Tim Goodman

8

Sizin yeni sorun, satırlar arasında azalmayan değerlerle, çözmek oldukça kolaydır.

Sol sütunun en yüksek sayıları içerdiğine dikkat edin. Bu nedenle, herhangi bir optimum çözüm önce bu sütunu sıfıra indirmelidir. Böylece, bu sütun üzerinde 1-B bombalama çalışması içindeki her elemanı sıfıra . Bombaların ikinci sütuna düşmesine izin veriyoruz, böylece maksimum hasar veriyorlar. Burada 1D davası ile ilgili birçok gönderi var, sanırım, bu davayı atlarken güvende hissediyorum. (Eğer tarif etmemi istiyorsan, yapabilirim.). Azalan özellik nedeniyle, en soldaki üç sütunun tümü sıfıra indirilecektir. Ancak, burada asgari sayıda bomba kullanacağız çünkü sol sütun sıfırlanmalıdır.

Şimdi, sol sütun sıfırlandığında, şimdi sıfırlanan en soldaki üç sütunu kesip şimdi indirgenmiş matrisle tekrarlıyoruz. Bu bize en uygun çözümü vermelidir, çünkü her aşamada asgari sayıda bomba kullanıyoruz.


Anladım. Benzer bir fikir düşündüm. : S Bir dahaki sefere daha dikkatli okuyacağım. Ancak bu sayede birçok insanın 'çözmesi gereken' güzel 'sorunu var.
abc

4

Mathematica Tamsayı Dal ve Bağ Kullanarak Doğrusal Programlama

Daha önce de belirtildiği gibi, bu problem tam sayı doğrusal programlama ( NP-Hard ) kullanılarak çözülebilir . Mathematica'da ILP zaten yerleşik. "To solve an integer linear programming problem Mathematica first solves the equational constraints, reducing the problem to one containing inequality constraints only. Then it uses lattice reduction techniques to put the inequality system in a simpler form. Finally, it solves the simplified optimization problem using a branch-and-bound method."[ Mathematica'da Kısıtlı Optimizasyon Eğiticisine bakın ..]

Mathematica ILP kütüphanelerini kullanan aşağıdaki kodu yazdım. Şaşırtıcı derecede hızlı.

solveMatrixBombProblem[problem_, r_, c_] := 
 Module[{}, 
  bombEffect[x_, y_, m_, n_] := 
   Table[If[(i == x || i == x - 1 || i == x + 1) && (j == y || 
        j == y - 1 || j == y + 1), 1, 0], {i, 1, m}, {j, 1, n}];
  bombMatrix[m_, n_] := 
   Transpose[
    Table[Table[
      Part[bombEffect[(i - Mod[i, n])/n + 1, Mod[i, n] + 1, m, 
        n], (j - Mod[j, n])/n + 1, Mod[j, n] + 1], {j, 0, 
       m*n - 1}], {i, 0, m*n - 1}]];
  X := x /@ Range[c*r];
  sol = Minimize[{Total[X], 
     And @@ Thread[bombMatrix[r, c].X >= problem] && 
      And @@ Thread[X >= 0] && Total[X] <= 10^100 && 
      Element[X, Integers]}, X];
  Print["Minimum required bombs = ", sol[[1]]];
  Print["A possible solution = ", 
   MatrixForm[
    Table[x[c*i + j + 1] /. sol[[2]], {i, 0, r - 1}, {j, 0, 
      c - 1}]]];]

Sorunda verilen örnek için:

solveMatrixBombProblem[{2, 3, 4, 7, 1, 1, 5, 2, 6, 2, 4, 3, 4, 2, 1, 2, 1, 2, 4, 1, 3, 1, 3, 4, 1, 2, 1, 4, 3, 2, 6, 9, 1, 6, 4}, 7, 5]

çıktılar

resim açıklamasını buraya girin

Açgözlü bir algoritma ile bunu okuyan herkes için

Aşağıdaki 10x10 sorununda kodunuzu deneyin:

5   20  7   1   9   8   19  16  11  3  
17  8   15  17  12  4   5   16  8   18  
4   19  12  11  9   7   4   15  14  6  
17  20  4   9   19  8   17  2   10  8  
3   9   10  13  8   9   12  12  6   18  
16  16  2   10  7   12  17  11  4   15  
11  1   15  1   5   11  3   12  8   3  
7   11  16  19  17  11  20  2   5   19  
5   18  2   17  7   14  19  11  1   6  
13  20  8   4   15  10  19  5   11  12

İşte virgülle ayrılmış:

5, 20, 7, 1, 9, 8, 19, 16, 11, 3, 17, 8, 15, 17, 12, 4, 5, 16, 8, 18, 4, 19, 12, 11, 9, 7, 4, 15, 14, 6, 17, 20, 4, 9, 19, 8, 17, 2, 10, 8, 3, 9, 10, 13, 8, 9, 12, 12, 6, 18, 16, 16, 2, 10, 7, 12, 17, 11, 4, 15, 11, 1, 15, 1, 5, 11, 3, 12, 8, 3, 7, 11, 16, 19, 17, 11, 20, 2, 5, 19, 5, 18, 2, 17, 7, 14, 19, 11, 1, 6, 13, 20, 8, 4, 15, 10, 19, 5, 11, 12

Bu sorun için benim çözüm 208 bomba içeriyor . İşte olası bir çözüm (bunu yaklaşık 12 saniyede çözebildim).

resim açıklamasını buraya girin

Mathematica'nın ürettiği sonuçları test etmenin bir yolu olarak, açgözlü algoritmanızın daha iyisini yapıp yapamayacağını görün.



3

Sorunu doğrusal alt problemlere dönüştürmeye gerek yoktur.

Bunun yerine, en büyüğünden başlayarak köşeleri bombalamak için basit bir açgözlü sezgisel kullanın .

Verilen örnekte dört köşe vardır, {2, 1, 6, 4}. Her köşe için köşeyi diyagonal hücrelere bombalamaktan daha iyi bir hareket yoktur, bu yüzden ilk 2 + 1 + 6 + 4 = 13 bombalamamızın bu diyagonal hücrelerde olması gerektiğini biliyoruz. Bombalamayı yaptıktan sonra yeni bir matris bırakıyoruz:

2 3 4 7 1      0 1 1 6 0      0 1 1 6 0     1 1 6 0     0 0 5     0 0 0 
1 5 2 6 2      0 3 0 5 1      0 3 0 5 1  => 1 0 4 0  => 0 0 3  => 0 0 0  
4 3 4 2 1      2 1 1 1 0      2 1 1 1 0     0 0 0 0     0 0 0     0 0 3  
2 1 2 4 1  =>  2 1 2 4 1  =>  2 1 2 4 1     0 0 3 0     0 0 3      
3 1 3 4 1      0 0 0 0 0      0 0 0 0 0 
2 1 4 3 2      0 0 0 0 0      0 0 0 0 0 
6 9 1 6 4      0 3 0 2 0      0 0 0 0 0 

İlk 13 bombardımandan sonra buluşma yöntemini üç bombardımanla 3 0 2 ortadan kaldırmak için kullanıyoruz. Şimdi 4. sırada 2 yeni köşemiz var, {2, 1}. Bunları bombalıyoruz, 3 bomba daha. Matrisi şimdi 4 x 4'e indirdik. Bir köşe, sol üst. Bunu bombalıyoruz. Şimdi 2 köşemiz kaldı, {5, 3}. 5 en büyük köşe olduğu için, önce 5 bombayı, sonra diğer köşede 3'ü bombalıyoruz. Toplam 13 + 3 + 3 + 1 + 5 + 3 = 28'dir.


1
Köşeleri bombaladıktan sonra genel olarak ne yaptığınızı anlamıyorum
RiaD

Köşeyi bombalamak, köşeden çapraz olarak içeri doğru bombalamaktan asla daha etkili değildir.
psr

1
Eğer benim yazı yanlış anlamak, köşeden çapraz bombalama, sonrası tekrar okuyorum
Tyler Durden

11
@TylerDurden: bu sadece matris küçük olduğu için çalışır. Daha büyük matrislerde, köşeyi bombaladıktan sonra, artık kenarları kesemezsiniz.
Yalan Ryan

3

Bu, konumların bu "labirentinde" en kısa yolu (bir dizi bombalama) araştırır. Hayır, daha hızlı bir algoritmanın olmadığını kanıtlayamıyorum, üzgünüm.

#!/usr/bin/env python

M = ((1,2,3,4),
     (2,3,4,5),
     (5,2,7,4),
     (2,3,5,8))

def eachPossibleMove(m):
  for y in range(1, len(m)-1):
    for x in range(1, len(m[0])-1):
      if (0 == m[y-1][x-1] == m[y-1][x] == m[y-1][x+1] ==
               m[y][x-1]   == m[y][x]   == m[y][x+1] ==
               m[y+1][x-1] == m[y+1][x] == m[y+1][x+1]):
        continue
      yield x, y

def bomb(m, (mx, my)):
  return tuple(tuple(max(0, m[y][x]-1)
      if mx-1 <= x <= mx+1 and my-1 <= y <= my+1
      else m[y][x]
      for x in range(len(m[y])))
    for y in range(len(m)))

def findFirstSolution(m, path=[]):
#  print path
#  print m
  if sum(map(sum, m)) == 0:  # empty?
    return path
  for move in eachPossibleMove(m):
    return findFirstSolution(bomb(m, move), path + [ move ])

def findShortestSolution(m):
  black = {}
  nextWhite = { m: [] }
  while nextWhite:
    white = nextWhite
    nextWhite = {}
    for position, path in white.iteritems():
      for move in eachPossibleMove(position):
        nextPosition = bomb(position, move)
        nextPath = path + [ move ]
        if sum(map(sum, nextPosition)) == 0:  # empty?
          return nextPath
        if nextPosition in black or nextPosition in white:
          continue  # ignore, found that one before
        nextWhite[nextPosition] = nextPath

def main(argv):
  if argv[1] == 'first':
    print findFirstSolution(M)
  elif argv[1] == 'shortest':
    print findShortestSolution(M)
  else:
    raise NotImplementedError(argv[1])

if __name__ == '__main__':
  import sys
  sys.exit(main(sys.argv))

1
Bu algoritma edecek hamle en az sayısını bulmak, ancak çok uzun bir zaman alabilir. Bunu verilen veri kümesinde çalıştırdınız mı? Bu, diğer algoritmaların karşılaştırılması için bir temel oluşturur.
Ryan Amos

1
Verilen matrisin 5x4'lük bir alt kümesi yaklaşık 2 saniye içinde çözüldü, 5x5 zaten 2 dakikadan fazla sürdü. Henüz denemedim ;-) Evet, bu algoritma orijinal görev dışında bir şey için optimize edilmemiştir: En kısa çözümü bulun.
Alfe

2
Üstel karmaşıklığın güzelliği böyledir.
Ryan Amos

3

Burada doğrusal bir programlama yaklaşımı çok yardımcı olabilir.

P m xn , konumların değerleri ile matris olsun :

Pozisyon matrisi

Şimdi tanımlayalım (x, y) bomba B matrisi mxn ile 1 ≤ x ≤ m , 1 ≤ y ≤ n aşağıda

Bomba matrisi

öyle ki

Bomba matrisindeki konumların değerleri

Örneğin:

B (3, 3)

Bu yüzden B m xn = [ b ij ] matrisini arıyoruz

  1. Bomba matrislerinin toplamı olarak tanımlanabilir:

    Bomba matrislerinin toplamı olarak B

    ( q ij o zaman p ij konumuna bırakacağımız bomba miktarı olurdu )

  2. p ij - b ij ≤ 0 (daha özlü olmak için P - B ≤ 0 olarak söyleyelim )

Ayrıca, B toplamı en aza indirmelidir bombaların toplamı.

B'yi ayrıca çirkin matris olarak da yazabiliriz :

Miktarların toplamı matrisi olarak B

ve P - B ≤ 0 (yani P ≤ B anlamına gelir ) beri aşağıdaki oldukça doğrusal eşitsizlik sistemine sahibiz:

Düşen bomba sayısı ve konumlardaki değerler arasındaki ilişki

Olmak q mn x 1 olarak tanımlanmaktadır

Miktar vektör

p mn x 1 olarak tanımlandı

Vektör olarak dağıtılan P değerleri

Biz bir sistemi var diyebiliriz matrisler http://latex.codecogs.com/gif.download?S%5Cmathbf%7Bq%7D&space;%5Cge&space;%5Cmathbf%7Bp%7D ürünü olarak temsil aşağıda sistemini olmanın S mn x mn sistemi çözmek için ters matris. Kendim genişletmedim ama kodda yapmanın kolay olması gerektiğine inanıyorum.

Şimdi asgari bir sorunumuz var.

Çözmemiz gereken sistem

Ben simpleks algoritması gibi bir şey ile çözülmesi kolay, neredeyse önemsiz bir şey olduğuna inanıyorum ( bu konuda oldukça güzel bir doktor var ). Ancak, neredeyse hiç doğrusal programlama bilmiyorum (Coursera'da bir ders alacağım ama sadece gelecekte ...), anlamaya çalışan bazı baş ağrılarım vardı ve bitirmek için büyük bir serbest işim var. sadece buradan vazgeç. Ben bir noktada bir şey yanlış yaptığını olabilir veya herhangi daha ileri gidemez, ama bu yol nihayetinde yol açabilir inanıyoruz çözümü. Her neyse, geri bildiriminiz için endişeliyim.

( Bu muhteşem siteye LaTeX ifadelerinden resim oluşturması için özel teşekkürler )


Eşitsizliklerinizin tersine çevrilmediğinden emin misiniz? Bu Sq> = P mi? yani bir karenin toplam bombalanma sayısı verilen matristen büyük veya ona eşittir .
darksky

1
Doğrusal bir programın değişkenleri tamsayılarla kısıtlandığında, buna "tamsayı doğrusal programlama" (IP) diyoruz . Sürekli durumun aksine, IP NP-Complete'tir. Ne yazık ki, bir yaklaşım kabul edilebilir olmadığı sürece simpleks algoritması yardımcı olmaz. IP'den daha önce başka bir cevapta bahsedildi .
BlueRaja - Danny Pflughoeft

@ BlueRaja-DannyPflughoeft doğru. "Despite the many crucial applications of this problem, and intense interest by researchers, no efficient algorithm is known for it.bkz. sayfa 254. Tamsayılı doğrusal programlama çok zor bir hesaplama problemidir. Verimli olmak için tek umudumuz, S matrisinizdeki içsel özelliklerden yararlanmaktır. Ne de olsa o kadar keyfi değil .
darksky

3

Bu açgözlü çözüm doğru görünüyor :

Yorumlarda belirtildiği gibi, 2D'de başarısız olacaktır. Ama belki onu geliştirebilirsiniz.

1D için:
En az 2 sayı varsa, en soldaki numaraya çekim yapmanız gerekmez, çünkü ikinciye çekim daha da kötü değildir . Yani ikinciye ateş et, ilk önce 0 değil, çünkü bunu yapmak zorundasın. Bir sonraki hücreye gitme. Son hücreyi unutma.

C ++ kodu:

void bombs(vector<int>& v, int i, int n){
    ans += n;
    v[i] -= n;
    if(i > 0)
        v[i - 1] -= n;
    if(i + 1< v.size())
        v[i + 1] -= n;
}

void solve(vector<int> v){
    int n = v.size();
    for(int i = 0; i < n;++i){
        if(i != n - 1){
            bombs(v, i + 1, v[i]);
        }
        else
            bombs(v, i, v[i])
    }
}

2D için:
Yine: ilk sırada çekim yapmanız gerekmez (ikincisi varsa). Yani ikincisine ateş edin. İlk satır için 1D görevini çözün. (çünkü boş bırakmanız gerekir). Aşağı in. Son satırı unutma.


5
Bir counterexample: "0110","1110","1110". Sadece 1 atışa ihtiyacınız var, ama algoritmanızın 2 kullanacağına inanıyorum.
maniek

2

Bomba sayısını en aza indirmek için her bombanın etkisini en üst düzeye çıkarmak zorundayız. Bunu başarmak için her adımda en iyi hedefi seçmeliyiz. Onu ve sekiz komşusunu toplayan her nokta için - bu noktayı bombalamanın verimlilik miktarı olarak kullanılabilir. Bu, optimum bomba dizisine yakın sağlayacaktır.

UPD : Bombaların verimsiz olduğu için sıfır sayısını da hesaba katmalıyız. Aslında sorun, vurulan sıfır sayısını en aza indirmektir. Ancak herhangi bir adımın bizi bu amaca nasıl yaklaştıracağını bilemeyiz. Sorunun NP-tamamlanmış olduğu fikrine katılıyorum. Gerçeğe yakın bir cevap verecek açgözlü bir yaklaşım öneriyorum.


Bu uygun değil. Karşı örnek: 1010101, 0010100(üst satır, alt sıra) Kişisel yaklaşım 3. gerektirecektir O 2. yapılabilir
Mysticial

2

Bomba miktarını en aza indirmek için sadece hasar miktarını en üst düzeye çıkarmanız gerektiğine inanıyorum .. bunun için en güçlü kuvvete sahip alanı kontrol etmeniz gerekiyor .. böylece önce 3x3 çekirdeği ile alanı analiz edip toplamın daha güçlü .. ve orada bomba .. ve alan düz oluncaya kadar yapın .. bunun için cevap 28

var oMatrix = [
[2,3,4,7,1],
[1,5,2,6,2],
[4,3,4,2,1],
[2,1,2,4,1],
[3,1,3,4,1],
[2,1,4,3,2],
[6,9,1,6,4]
]

var nBombs = 0;
do
{
    var bSpacesLeftToBomb = false;
    var nHigh = 0;
    var nCellX = 0;
    var nCellY = 0;
    for(var y = 1 ; y<oMatrix.length-1;y++) 
        for(var x = 1 ; x<oMatrix[y].length-1;x++)  
        {
            var nValue = 0;
            for(var yy = y-1;yy<=y+1;yy++)
                for(var xx = x-1;xx<=x+1;xx++)
                    nValue += oMatrix[yy][xx];

            if(nValue>nHigh)
            {
                nHigh = nValue;
                nCellX = x;
                nCellY = y; 
            }

        }
    if(nHigh>0)
    {
        nBombs++;

        for(var yy = nCellY-1;yy<=nCellY+1;yy++)
        {
            for(var xx = nCellX-1;xx<=nCellX+1;xx++)
            {
                if(oMatrix[yy][xx]<=0)
                    continue;
                oMatrix[yy][xx] = --oMatrix[yy][xx];
            }
        }
        bSpacesLeftToBomb = true;
    }
}
while(bSpacesLeftToBomb);

alert(nBombs+'bombs');

Bu, diğer cevapların birkaçıyla aynı algoritmadır, ancak daha sonra.
psr

@psr Sadece bu değil. Optimal değil.
Gizemli

Bunu gönderdim, çünkü bu algoritma önerilmiş olsa da, herhangi bir kod ya da "kavram prof." Bulmadım. bu yüzden bu tartışmaya yardımcı olabileceğini düşündüm .. ama .. btw @Mysticial daha uygun bir yol var prof var mı?
CaldasGSM

@CaldasGSM Endişelenmeyin, orijinal sorun (sıralama olmadan) zordur. Şimdiye kadar onu en iyi şekilde çözen tek bir cevap var , ancak üstel zamanda çalışıyor.
Gizemli

2

İşte köşelerin iyi özelliklerini genelleştiren bir çözüm.

Belirli bir alan için mükemmel bir düşüş noktası bulabileceğimizi varsayalım, yani içindeki değeri azaltmanın en iyi yolu. Daha sonra bırakılacak minimum bomba sayısını bulmak için, bir algoritmanın ilk taslağı olabilir (kod bir ruby ​​uygulamasından kopyalanır):

dropped_bomb_count = 0
while there_are_cells_with_non_zero_count_left
  coordinates = choose_a_perfect_drop_point
  drop_bomb(coordinates)
  dropped_bomb_count += 1
end
return dropped_bomb_count

Zor olan choose_a_perfect_drop_point. İlk olarak, mükemmel bir düşüş noktasının ne olduğunu tanımlayalım.

  • Bir damlama noktası için (x, y)bir değer azalır(x, y) . Ayrıca diğer hücrelerdeki değerleri de düşürebilir.
  • Bir damlama noktası , bir için (x, y)olan iyi bir damlama noktası daha b için (x, y)bu hücrelerin bu uygun bir üst dizisi değerleri azalırsa b azalır.
  • Bir bırakma noktası maksimumdurBaşka bir daha iyi bırakma noktası yoksa, .
  • İki damla noktaları (x, y)vardır eşdeğer onlar hücrelerin aynı setini azaltmak eğer.
  • İçin bir damla noktası (x, y)olan mükemmel bunun için tüm maksimal damla noktalarına denk olup olmadığını (x, y).

Mükemmel bir düşme noktası (x, y)varsa, değeri (x, y)mükemmel düşme noktalarından birine bomba atmaktan daha etkili bir şekilde düşüremezsiniz (x, y).

Belirli bir alan için mükemmel bir bırakma noktası, hücrelerinin herhangi biri için mükemmel bir bırakma noktasıdır.

İşte birkaç örnek:

1 0 1 0 0
0 0 0 0 0
1 0 0 0 0
0 0 0 0 0
0 0 0 0 0

Hücre (0, 0)(sıfır bazlı indeks) için mükemmel bir düşme noktasıdır (1, 1). Tüm diğer damla noktaları (1, 1)olduğunu (0, 0), (0, 1)ve (1, 0), daha az hücreleri azalır.

0 0 0 0 0
0 0 0 0 0
0 0 1 0 0
0 0 0 0 0
0 0 0 0 0

Hücre için mükemmel bir damla noktası (2, 2)(sıfır tabanlı dizin) 'dir (2, 2)ve aynı zamanda tüm çevre hücreler (1, 1), (1, 2), (1, 3), (2, 1), (2, 3), (3, 1), (3, 2), ve (3, 3).

0 0 0 0 1
0 0 0 0 0
0 0 1 0 0
0 0 0 0 0
0 0 0 0 0

hücre için kusursuz bir damla noktaları (2, 2)olan (3, 1): Bu değer azalır (2, 2)ve değeri (4, 0). İçin diğer tüm bırakma noktaları (2, 2)maksimum değildir, çünkü bir hücreyi daha az azaltırlar. İçin mükemmel damla noktası (2, 2)aynı zamanda mükemmel bir damla noktasıdır (4, 0)ve alan için tek mükemmel damla noktasıdır. Bu alan için mükemmel bir çözüm sağlar (bir bomba düşmesi).

1 0 0 0 0
0 0 0 0 0
0 0 1 0 0
0 0 0 0 0
1 0 0 0 0

Hiçbir mükemmel damla noktası içindir (2, 2): Her iki (1, 1)ve (1, 3)azalış (2, 2)ve başka hücre (onlar için maksimal damla noktalarıdır (2, 2)), ancak bunlar eşdeğer değildir. Bununla birlikte, (1, 1)için mükemmel bir damla noktasıdır (0, 0)ve (1, 3)için mükemmel bir damla noktasıdır (0, 4).

Bu mükemmel düşme noktalarının tanımı ve belirli bir kontrol sırası ile, sorudaki örnek için aşağıdaki sonucu elde ederim:

Drop bomb on 1, 1
Drop bomb on 1, 1
Drop bomb on 1, 5
Drop bomb on 1, 5
Drop bomb on 1, 5
Drop bomb on 1, 6
Drop bomb on 1, 2
Drop bomb on 1, 2
Drop bomb on 0, 6
Drop bomb on 0, 6
Drop bomb on 2, 1
Drop bomb on 2, 5
Drop bomb on 2, 5
Drop bomb on 2, 5
Drop bomb on 3, 1
Drop bomb on 3, 0
Drop bomb on 3, 0
Drop bomb on 3, 0
Drop bomb on 3, 0
Drop bomb on 3, 0
Drop bomb on 3, 4
Drop bomb on 3, 4
Drop bomb on 3, 3
Drop bomb on 3, 3
Drop bomb on 3, 6
Drop bomb on 3, 6
Drop bomb on 3, 6
Drop bomb on 4, 6
28

Bununla birlikte, algoritma sadece her adımdan sonra en az bir mükemmel düşüş noktası varsa çalışır. Mükemmel düşme noktalarının bulunmadığı örnekler oluşturmak mümkündür:

0 1 1 0
1 0 0 1
1 0 0 1
0 1 1 0

Bu durumlarda, algoritmayı, mükemmel bir bırakma noktası yerine, minimum maksimum bırakma noktası seçeneğine sahip bir koordinat seçip her seçim için minimum değeri hesaplayabilmemiz için değiştirebiliriz. Yukarıdaki durumda, değerleri olan tüm hücrelerin iki maksimum bırakma noktası vardır. Örneğin (0, 1), maksimum düşme noktalarına sahiptir (1, 1)ve (1, 2). İkisinden birini seçmek ve ardından minimum kalsual hesaplamak bu sonuca yol açar:

Drop bomb on 1, 1
Drop bomb on 2, 2
Drop bomb on 1, 2
Drop bomb on 2, 1
2

Bu, yukarıda sunulan açgözlü algoritmadır.
darksky

Bu da açgözlü bir algoritma, ancak köşelere ve kenarlara odaklanmak yerine, bir sonraki bırakma noktasının nasıl seçileceğini tanımladım. 5x7 örnek karesiyle, 1000x1000 alanda köşelerden bahsetmek çok kolay değil. Algoritmamın alanı temizleme sırasını kontrol ederseniz, alan dışarıdan değil, yukarıdan aşağıya / soldan sağa doğrudur.
Tammo Freese

2

İşte başka bir fikir:

Tahtaya her bir alana bir bomba bırakarak kaç tane sayı azaltılacağına bir ağırlık atayarak başlayalım. Boşluk sıfırdan farklı bir sayıya sahipse, bir puan alır ve ona bitişik herhangi bir boşluk sıfırdan farklı bir sayıya sahipse, ek bir puan alır. Yani 1000'e 1000 ızgara varsa, 1 milyon alanın her birine atanan bir ağırlığımız var.

Sonra boşlukların listesini ağırlıkça sıralayın ve en yüksek ağırlığa sahip olanı bombalayın. Yani, bizim paranın karşılığını en iyi şekilde alıyor.

Bundan sonra, ağırlığı bombadan etkilenen her alanın ağırlığını güncelleyin. Bu bombaladığınız alan ve ona hemen bitişik olan herhangi bir alan ve hemen bunlara bitişik olan herhangi bir alan olacaktır. Başka bir deyişle, bombalama ile değeri sıfıra düşürülebilen herhangi bir alan veya komşu bir alanın değeri sıfıra düşürüldü.

Ardından, liste boşluklarını ağırlıklarına göre yeniden sıralayın. Sadece küçük bir uzay alt kümesi, bombalama ile ağırlıklarını değiştirdiği için, tüm listeye başvurmanıza gerek yok, sadece bunları listede hareket ettirin.

Yeni en yüksek ağırlık alanını bombalayın ve prosedürü tekrarlayın.

Bu, her bombalamanın mümkün olduğu kadar çok alanı azaltmasını garanti eder (temel olarak, mümkün olduğunca sıfır olan az sayıda alana vurur), bu nedenle, ağırlıklarında bağlar olabilmesi dışında, optimal olacaktır. Bu nedenle, en yüksek ağırlık için bir kravat olduğunda biraz geri izleme yapmanız gerekebilir. Bununla birlikte, sadece en yüksek ağırlık için bir kravat önemlidir, ancak diğer bağlar için değil, bu yüzden umarım çok fazla geri izleme değildir.

Düzenleme: Mysticial'ın aşağıdaki karşı örneği, aslında bu, ağırlıklardaki bağlara bakılmaksızın, optimal olduğu garanti edilmediğini göstermektedir. Bazı durumlarda, belirli bir adımda ağırlığı olabildiğince azaltmak, ilk adımda biraz daha az açgözlü bir seçime sahip olabileceğiniz gibi, ikinci adımdan sonra kümülatif bir azalma elde etmek için kalan bombaların çok fazla yayılmasını sağlar. Sonuçların bombalama sırasına karşı duyarsız olduğu fikrinden biraz yanıltıcıydım. Onlar şunlardırherhangi bir bombalama dizisini alıp başlangıçtan farklı bir sırayla tekrar oynatabileceğiniz ve sonuçta ortaya çıkan aynı pano ile sırayla duyarsızsınız. Ancak her bombalamayı bağımsız olarak düşünebileceğinizden yola çıkmaz. Veya en azından her bombalama işlemi, sonraki bombalamalar için tahtayı ne kadar iyi ayarladığını dikkate alacak şekilde düşünülmelidir.


Alanlar çok az sıfır olduğundan, başlangıçta hala çok fazla geri izleme olacak, çoğu hücrenin ağırlıkları tamamen dokuz olacak.
Yalan Ryan

Evet, bu iyi bir nokta, çünkü olası ağırlıklarda büyük bir aralık yok (sadece 0 ila 9).
Tim Goodman

Geri izlemenin ne kadar gerekli olduğundan hala% 100 emin değilim ... açgözlü bombalamanın bir seçiminin açgözlü bombalamanın başka bir seçiminden daha düşük olduğu bir ızgara oluşturmak öğretici olabilir. Belki de hangisinin daha iyi olduğunu tahmin etmenin tutarlı bir yolu vardır.
Tim Goodman

Aslında Albay Paniç'in bunu cevabında yaptığını görüyorum. Açgözlü bir seçimin diğerinden daha iyi olmasının nedeni, kalan sayıların daha fazla yayılmasıdır.
Tim Goodman

3
1010101, 0010100bu yaklaşımın optimal olmadığını kanıtlayan bir karşı örnek olabilir. Bu yaklaşım 3 gerektirir. - 2'de yapılabilir.
Mistik

1

Diyelim ki tahta konumlarını 1, 2, ..., nx m olarak numaralandırıyoruz. Herhangi bir bomba damlası dizisi, sayıların tekrarlanabileceği bu kümedeki bir sayı dizisiyle temsil edilebilir. Bununla birlikte, bombaları hangi sırada bıraktığınıza bakılmaksızın tahta üzerindeki etki aynıdır, bu yüzden gerçekten herhangi bir bomba damlası seçeneği, nxm sayılarının bir listesi olarak temsil edilebilir; burada ilk sayı, konum 1'de düşen bomba sayısını temsil eder , ikinci sayı 2. pozisyonda vb. atılan bomba sayısını temsil eder. Bu nxm numaraları listesine "anahtar" diyelim.

Önce 1 bomba düşmesinden kaynaklanan tüm pano durumlarını hesaplamayı deneyebilir, ardından bunları sıfır bomba elde edene kadar 2 bomba damlasından kaynaklanan tüm pano durumlarını hesaplamak için kullanabilirsiniz. Ancak her adımda, yukarıda tanımladığım anahtarı kullanarak durumları önbelleğe alırsınız, böylece bir sonraki adımı ("dinamik programlama" yaklaşımı) hesaplarken bu sonuçları kullanabilirsiniz.

Ancak n, m boyutuna ve ızgaradaki sayılara bağlı olarak, bu yaklaşımın bellek gereksinimleri aşırı olabilir. N + 1 için tüm sonuçları hesapladıktan sonra N bomba düşmesi için tüm sonuçları atabilirsiniz, böylece orada bazı tasarruflar vardır. Ve elbette çok daha uzun sürmesi pahasına hiçbir şey önbellekleyemezdiniz - dinamik programlama yaklaşımı hız için bellek ticareti yapar.


1
Şüphe bu yana mümkün (eğer seni doğru anladıysam). n = m. (10 ^ 6) ^ 2 int hücrelere 10 ^ 6 int işaretçiler gerekir. Tablodaki anahtarlar kadar pano var. 10 ^ 12 şüphe 32bit makinede çok fazla tahsis edebilirim.
abc

Evet, tahtaların 1000'e 1000'e kadar olduğu hakkındaki yorumunuzu gördüm. Bu, her bir panonun durumu için bir milyon ints, her bir pozisyona düşen bomba sayısı için bir milyon ints. Bu nedenle, depoladığınız her kart için 2 milyon inta ihtiyacınız var ve birçok olası kart var ...
Tim Goodman

Farklı bir yaklaşım kullanan ikinci bir cevap ekledim.
Tim Goodman

1
Evet. Bir tür kaba kuvvet yaklaşımı, ama sanırım büyük bir tahta için çok pratik değil.
Tim Goodman

@Kostek, neden bu kadar düşük bir tahmin? Daha çok k ^ (m * n) bellek gibidir, k, kartın başlangıçta doldurulduğu sayılar için sınırdır.
Rotsor

1

Tahtayı temizlemek için mutlak optimal çözüm istiyorsanız, klasik backtracking kullanmak zorunda kalacaksınız, ancak matris çok büyükse, en iyi çözümü bulmak için zaman alacaktır, eğer "olası" optimal bir çözüm istiyorsanız, açgözlü algoritma kullanabilirsiniz , algoritmayı yazmak için yardıma ihtiyacınız varsa size yardımcı olabilirim

Bunun en iyi yolu olduğunu düşünmeye gel. Oraya başka bir matris yapın, oraya bir bomba bırakarak çıkardığınız noktaları saklayın ve maksimum puanlı hücreyi seçin ve puan matrisini güncelleyerek bomba bırakın. Misal:

2 3 5 -> (2+(1*3)) (3+(1*5)) (5+(1*3))
1 3 2 -> (1+(1*4)) (3+(1*7)) (2+(1*4))
1 0 2 -> (1+(1*2)) (0+(1*5)) (2+(1*2))

0'dan yüksek bir değere sahip her bitişik hücre için +1 hücre değeri


7
klasik backtracking kullanmak zorunda kalacak . Bunun için bir kanıtın var mı?
Shahbaz

Emin değilim. Hazırlandığım yarışmadan (önceki yıldan). Sınırlar 1 <= n, m <= 1000'dir (büyük olup olmadığını bilmiyorum). Her neyse, kesin cevaba ihtiyacınız var (CERC yarışmasına benzer). Yarışma sayfasında da zaman sınırı, cevap yok, çözüm bulunmuyor.
abc

diğer tüm algoritmalar size olası en uygun çözümü sunacaktır, ancak hepsini
deneyene

2
arama izniniz olduğu için permtrayon değil, geri izlemeyi kullanmanız gerekmez. Bomba atma sırası önemli değil
Luka Rahne

o zaman açgözlü bir varyasyon kullanmayı deneyebilirsiniz. her adımda yeni bir matris yaratın ve her nokta, yanındaki her hücre için hücresinin değerine + 1 sahip olacaktır> 0 bu şekilde bir sonraki bombaları nereye bırakacağınızı daha iyi
seçecektir

1

Kaba kuvvet !

Etkili olmadığını biliyorum, ancak daha hızlı bir algoritma bulsanız bile, ne kadar doğru olduğunu bilmek için her zaman bu sonuca karşı test edebilirsiniz.

Bunun gibi bir özyineleme kullanın:

void fn(tableState ts, currentlevel cl)
{
  // first check if ts is all zeros yet, if not:
  //
  // do a for loop to go through all cells of ts, 
  // for each cell do a bomb, and then
  // call: 
  // fn(ts, cl + 1);

}

Önbelleğe alarak bunu daha verimli hale getirebilirsiniz, farklı bir yol aynı sonuca yol açarsa, aynı adımları tekrarlamamalısınız.

Detaylandırmak için:

1,3,5 bombalama hücresi, 5,3,1 bombalama hücresi ile aynı sonuca yol açarsa, her iki durumda da bir sonraki adımları tekrarlamamalısınız, sadece 1 yeterlidir, hepsini bir yerde saklamalısınız Tablo durumlarını ve sonuçlarını kullanır.

Hızlı karşılaştırma yapmak için bir karma tablo istatistikleri kullanılabilir.


1
  1. Sınırı asla bombalama (meydanda sınırsız komşu yoksa)
  2. Sıfır köşe.
  3. Sıfır köşeye, köşenin köşegen değerini bir kare uzağa bırakın (tek sınır olmayan komşu)
  4. Bu yeni köşeler yaratacaktır. 2'ye git

Düzenleme: Kostek'in neredeyse aynı yaklaşımı önerdiğini fark etmedim, bu yüzden şimdi daha güçlü bir iddiada bulunuyorum: Temizlenecek köşeler her zaman en dış katmanda seçildiyse, o zaman en iyisidir.

OP örneğinde: 5'ten başka herhangi bir şeye 2 (1 + 1 veya 2 olarak) düşürmek, 5'e düşürmenin karesini vurmasına yol açmaz. Bu yüzden sadece 5'e 2 (ve sol altta 6 ...) düşürmeliyiz.

Bundan sonra, başlangıçta 1 (şimdi 0) olan köşeyi (sol üstte) temizlemenin tek bir yolu var ve bu da B3'e 0 düşerek (gösterim gibi excel). Ve bunun gibi.

Sadece tüm A ve E sütunlarını ve 1 ve 7 satırları temizledikten sonra, bir katmanı daha derine temizlemeye başlayın.

Sadece kasıtlı olarak temizlenmiş olanları temizlediklerini düşünün, 0 değer köşelerini temizlemenin hiçbir maliyeti yoktur ve düşünmeyi basitleştirir.

Bu şekilde atılan tüm bombalar düşürülmeli ve bu da temizlenmiş alanlara yol açtığı için en uygun çözümdür.


İyi bir uykudan sonra bunun doğru olmadığını fark ettim. Düşünmek

  ABCDE    
1 01000
2 10000
3 00000
4 00000

Yaklaşımım B3 ve C2'ye bomba atacak, B2'ye düşmek yeterli olurdu


Ama bu optimal mi?
Gizemli

7
Yeni köşeler 2 şekilde bombalanabilir (çoğu köşe noktası 4 değerin en altındaysa). Hangisi optimum bombalama?
abc

Benzer bir yaklaşım düşünüyordum ve Kostek gibi bir duruma geldiğinizde, geri izlemeyi kullanmaya başlayın ...
Karoly Horvath

Köşeler, diyagonal kareye bırakılacak minimum miktarda bomba verir. Ancak onları sıfırladıktan sonra, bir sonraki sınır döşemesinin belirgin bir optimal noktasına sahip olması gerekmez. Yine de arama alanını azaltmanın iyi bir yoludur.
Eugene

İsabet kutusunda en yüksek toplam sayıyı veren yeni köşe köşegenini seçmeye ne dersiniz?
Yargıç Maygarden

1

İşte benim çözümüm .. Henüz zamanım olmadığı için henüz kodda yazmayacağım, ama bunun her seferinde optimal sayıda hamle üretmesi gerektiğine inanıyorum - yine de bulmada ne kadar verimli olacağından emin değilim bomba noktaları.

Birincisi, @Luka Rahne'nin yorumlardan birinde belirttiği gibi, bombalama sırası önemli değil - sadece kombinasyon.

İkincisi, diğer birçok kişinin belirttiği gibi, köşelerden köşegen 1-off bombalama en uygunudur çünkü köşelerden daha fazla noktaya temas eder.

Bu, algoritma sürümümün temelini oluşturuyor: İlk önce veya son olarak 'köşelerden 1-off'u bombalayabiliriz, önemli değil (teoride) Bunları önce bombalıyoruz çünkü daha sonraki kararları kolaylaştırıyor (uygulamada) Aynı anda bu köşeleri bombalarken en fazla puanı etkileyen noktayı bombalıyoruz.

En tanımlayalım Resistance Olan Bölgeler ile kurulunda puan olması en olmayan bombable noktaları + 0 yılların en fazla sayıda çevrelerindeki

bombardıman edilemeyen noktalar , bakmakta olduğumuz yönetim kurulu kapsamımızda mevcut olmayan noktalar olarak tanımlanabilir .

Ayrıca kapsamımızı işleyecek 4 sınır tanımlayacağım: Üst = 0, Sol = 0, Alt = k, sağ = j. (başlayacak değerler)

Son olarak, optimum bombaları , direnç noktalarına bitişik olan ve (1) en değerli direnç noktasına ve (2) mümkün olan en fazla puana temas eden bombalar olarak tanımlayacağım .

Yaklaşımla ilgili olarak dışarıdan çalıştığımız açık. 4 'bombardıman uçağı' ile aynı anda çalışabileceğiz.

İlk direniş noktaları açıkça bizim köşelerimizdir. 'Sınır dışı' noktalar bombalanamaz (her köşe için kapsam dışında 5 puan vardır). Bu yüzden noktaları önce köşelerden biri çapraz olarak bombalıyoruz.

Algoritma:

  1. En uygun 4 bomba noktasını bulun.
  2. Bir bomba noktası 2 sınıra (yani bir köşeye) temas eden bir direnç noktasını bombalıyorsa, o noktaya kadar bomba 0 olur. Aksi takdirde, optimum bomba noktasına dokunan direnç noktalarından biri 0 olana kadar her birini bombalayın.
  3. her sınır için: if (toplam (bağlı) == 0) ileri sınır

TOP = ALT ve SOL = SAĞ kadar tekrarlayın

Daha sonra gerçek kodu yazmaya çalışacağım


1

Durum alanı planlamasını kullanabilirsiniz. Örneğin, f = g + hböyle bir buluşsal yöntemle birleştirilmiş A * (veya varyantlarından birini) kullanarak :

  • g: şimdiye kadar atılan bomba sayısı
  • h: ızgaranın tüm değerlerinin 9'a bölünmesiyle elde edilen toplamı (en iyi sonuç, yani kabul edilebilir bir buluşsal yöntemimiz olduğu anlamına gelir)

1

28 hamle de yaptım. Bir sonraki en iyi hamle için iki test kullandım: ilk önce tahta için minimum toplamı üreten hamle. İkincisi, eşit toplamlar için, maksimum yoğunluğu üreten hareket şu şekilde tanımlanır:

number-of-zeros / number-of-groups-of-zeros

Bu Haskell. "çözmek kurulu" motorun çözümünü gösterir. "Ana" yazarak oyunu oynayabilir, sonra bir hedef nokta, bir öneri için "en iyi" veya "çıkmak için" çıkabilirsiniz.

ÇIKTI:
* Ana> çözme kartı
[(4,4), (3,6), (3,3), (2,2), (2,2), (4,6), (4,6), (2,6), (3,2), (4,2), (2,6), (3,3), (4,3), (2,6), (4,2), (4 , 6), (4,6), (3,6), (2,6), (2,6), (2,4), (2,4), (2,6), (3,6 ), (4,2), (4,2), (4,2), (4,2)]

import Data.List
import Data.List.Split
import Data.Ord
import Data.Function(on)

board = [2,3,4,7,1,
         1,5,2,6,2,
         4,3,4,2,1,
         2,1,2,4,1,
         3,1,3,4,1,
         2,1,4,3,2,
         6,9,1,6,4]

n = 5
m = 7

updateBoard board pt =
  let x = fst pt
      y = snd pt
      precedingLines = replicate ((y-2) * n) 0
      bomb = concat $ replicate (if y == 1
                                    then 2
                                    else min 3 (m+2-y)) (replicate (x-2) 0 
                                                         ++ (if x == 1 
                                                                then [1,1]
                                                                else replicate (min 3 (n+2-x)) 1)
                                                                ++ replicate (n-(x+1)) 0)
  in zipWith (\a b -> max 0 (a-b)) board (precedingLines ++ bomb ++ repeat 0)

showBoard board = 
  let top = "   " ++ (concat $ map (\x -> show x ++ ".") [1..n]) ++ "\n"
      chunks = chunksOf n board
  in putStrLn (top ++ showBoard' chunks "" 1)
       where showBoard' []     str count = str
             showBoard' (x:xs) str count =
               showBoard' xs (str ++ show count ++ "." ++ show x ++ "\n") (count+1)

instances _ [] = 0
instances x (y:ys)
  | x == y    = 1 + instances x ys
  | otherwise = instances x ys

density a = 
  let numZeros = instances 0 a
      groupsOfZeros = filter (\x -> head x == 0) (group a)
  in if null groupsOfZeros then 0 else numZeros / fromIntegral (length groupsOfZeros)

boardDensity board = sum (map density (chunksOf n board))

moves = [(a,b) | a <- [2..n-1], b <- [2..m-1]]               

bestMove board = 
  let lowestSumMoves = take 1 $ groupBy ((==) `on` snd) 
                              $ sortBy (comparing snd) (map (\x -> (x, sum $ updateBoard board x)) (moves))
  in if null lowestSumMoves
        then (0,0)
        else let lowestSumMoves' = map (\x -> fst x) (head lowestSumMoves) 
             in fst $ head $ reverse $ sortBy (comparing snd) 
                (map (\x -> (x, boardDensity $ updateBoard board x)) (lowestSumMoves'))   

solve board = solve' board [] where
  solve' board result
    | sum board == 0 = result
    | otherwise      = 
        let best = bestMove board 
        in solve' (updateBoard board best) (result ++ [best])

main :: IO ()
main = mainLoop board where
  mainLoop board = do 
    putStrLn ""
    showBoard board
    putStr "Pt: "
    a <- getLine
    case a of 
      "quit"    -> do putStrLn ""
                      return ()
      "best"    -> do putStrLn (show $ bestMove board)
                      mainLoop board
      otherwise -> let ws = splitOn "," a
                       pt = (read (head ws), read (last ws))
                   in do mainLoop (updateBoard board pt)

1

Burada iki taraflı bir eşleştirme altyapısı var gibi görünüyor. Aşağıdaki örneği düşünün:

0010000
1000100
0000001
1000000
0000001
1000100
0010000

Bu durum için en uygun çözüm 5 büyüklüğüne sahiptir, çünkü bu, 9 döngüsünün köşelerinin minimum kaplamasının boyutu kenarlarıdır.

Bu durum, özellikle, birkaç kişinin yayınladığı doğrusal programlama gevşemesinin tam olmadığını, işe yaramadığını ve diğer tüm kötü şeyleri göstermektedir. Sorunuza "düzlemsel kübik grafiğimin köşelerini olabildiğince az kenarla kapatabileceğime" eminim, bu da açgözlü / tepe tırmanma çözümlerinden herhangi birinin işe yarayıp yaramayacağından şüphe duymamı sağlıyor.

En kötü durumda bunu polinom zamanında çözmenin bir yolunu göremiyorum. Görmediğim çok akıllı bir ikili arama ve DP çözümü olabilir.

DÜZENLEME : Görüyorum ki yarışma ( http://deadline24.pl ) dile bağlı değil; size bir sürü girdi dosyası gönderir ve onlara çıktılar gönderirsiniz. Yani en kötü polinom zamanında çalışan bir şeye ihtiyacınız yok. Özellikle, girdiye bakacaksınız !

Girişte bir sürü küçük vaka var. Sonra 10x1000 kasa, 100x100 kasa ve 1000x1000 kasa var. Üç büyük vakanın hepsi çok iyi davrandı. Yatay olarak bitişik girişler tipik olarak aynı değere sahiptir. Nispeten etli bir makinede, tüm vakaları sadece birkaç dakika içinde CPLEX kullanarak kaba kuvvet uygulayarak çözebilirim. 1000x1000'de şanslıydım; LP gevşemesi, entegre bir optimal çözüme sahiptir. Çözümlerim .anstest veri paketinde sağlanan dosyaları kabul ediyor .

Bahse girerim, girdinin yapısına bir göz attıysanız yaptığımdan çok daha doğrudan bir şekilde kullanabilirsiniz; hiçbir şey kalmayıncaya kadar ilk satırı veya iki veya üç tanesini tekrar tekrar sıralayabileceğiniz anlaşılıyor. (1000x1000'de tüm satırlar artmıyor mu? Sanırım "B parçanız" nereden geliyor?)


Evet. Bazen metnin "alakasız" kısmını atlarım. Sadece kısaca fikir edin. Bu kez temelde kolay cehennem gibi zordan değiştirmek: P Her neyse biliyorum bir sezgisel "bilinen" girdi kümesi yapmaya çalışabilirsiniz. Öte yandan, eğer cevap yüzde puan değilse, 5 saat boyunca kolayca performans gösterecek bir algoritma olması gerektiğini düşünüyorum. Bulduğum tek şeyin çok büyük bir karmaşıklığı vardı. Sonra birileri kökeni sorduğunda daha dikkatli okudum :)
abc

Pek çok insanın düşünmesi gereken güzel bir sorunu olduğu için söyleyebiliriz, ancak polinom zamanında yapılabileceğinden şüphe duyuyoruz. Basit bir kısıtlamanın, görev düzeyini kolaydan imkansız hale nasıl değiştirdiği komik.
abc

@Kostek: Belirsiz olsaydım özür dilerim. Ben ... izleyicilere uygun seviyelerde açıklamalar yapmakta oldukça kötüyüm. :) Nerede belirsizdim?
tmyklebu

1

Sadece en iyi buluşsal yöntemimi kullanarak bombalama kampanyasını hesaplamaksızın gerçek sayıyı hesaplamanın bir yolunu düşünemiyorum ve makul bir sonuç alacağımı umuyorum.

Bu yüzden benim yöntemim her hücre için bir bombalama verimliliği metriği hesaplamak, en yüksek değere sahip hücreyi bombalamak, .... her şeyi düzleştirene kadar süreci tekrarlamak. Bazıları bir metrik olarak basit potansiyel hasarı (yani 0'dan 9'a kadar puan) kullanmayı savunuyor, ancak bu, yüksek değerli hücreleri çarparak ve üst üste binme hasarını kullanmamakla yetersiz kalıyor. Ben hesaplamak cell value - sum of all neighbouring cells, herhangi bir pozitif 0 sıfırlayın ve negatif bir şey mutlak değerini kullanın. Sezgisel olarak bu metrik, doğrudan saymak yerine yüksek sayım içeren hücrelerdeki hasar çakışmasını en üst düzeye çıkarmaya yardımcı olan bir seçim yapmalıdır.

Aşağıdaki kod, 28 bombada test alanının tamamen tahrip olmasına ulaşıyor (metrik olarak potansiyel hasar kullanmanın 31!

using System;
using System.Collections.Generic;
using System.Linq;

namespace StackOverflow
{
  internal class Program
  {
    // store the battle field as flat array + dimensions
    private static int _width = 5;
    private static int _length = 7;
    private static int[] _field = new int[] {
        2, 3, 4, 7, 1,
        1, 5, 2, 6, 2,
        4, 3, 4, 2, 1,
        2, 1, 2, 4, 1,
        3, 1, 3, 4, 1,
        2, 1, 4, 3, 2,
        6, 9, 1, 6, 4
    };
    // this will store the devastation metric
    private static int[] _metric;

    // do the work
    private static void Main(string[] args)
    {
        int count = 0;

        while (_field.Sum() > 0)
        {
            Console.Out.WriteLine("Round {0}:", ++count);
            GetBlastPotential();
            int cell_to_bomb = FindBestBombingSite();
            PrintField(cell_to_bomb);
            Bomb(cell_to_bomb);
        }
        Console.Out.WriteLine("Done in {0} rounds", count);
    } 

    // convert 2D position to 1D index
    private static int Get1DCoord(int x, int y)
    {
        if ((x < 0) || (y < 0) || (x >= _width) || (y >= _length)) return -1;
        else
        {
            return (y * _width) + x;
        }
    }

    // Convert 1D index to 2D position
    private static void Get2DCoord(int n, out int x, out int y)
    {
        if ((n < 0) || (n >= _field.Length))
        {
            x = -1;
            y = -1;
        }
        else
        {
            x = n % _width;
            y = n / _width;
        }
    }

    // Compute a list of 1D indices for a cell neighbours
    private static List<int> GetNeighbours(int cell)
    {
        List<int> neighbours = new List<int>();
        int x, y;
        Get2DCoord(cell, out x, out y);
        if ((x >= 0) && (y >= 0))
        {
            List<int> tmp = new List<int>();
            tmp.Add(Get1DCoord(x - 1, y - 1));
            tmp.Add(Get1DCoord(x - 1, y));
            tmp.Add(Get1DCoord(x - 1, y + 1));
            tmp.Add(Get1DCoord(x, y - 1));
            tmp.Add(Get1DCoord(x, y + 1));
            tmp.Add(Get1DCoord(x + 1, y - 1));
            tmp.Add(Get1DCoord(x + 1, y));
            tmp.Add(Get1DCoord(x + 1, y + 1));

            // eliminate invalid coords - i.e. stuff past the edges
            foreach (int c in tmp) if (c >= 0) neighbours.Add(c);
        }
        return neighbours;
    }

    // Compute the devastation metric for each cell
    // Represent the Value of the cell minus the sum of all its neighbours
    private static void GetBlastPotential()
    {
        _metric = new int[_field.Length];
        for (int i = 0; i < _field.Length; i++)
        {
            _metric[i] = _field[i];
            List<int> neighbours = GetNeighbours(i);
            if (neighbours != null)
            {
                foreach (int j in neighbours) _metric[i] -= _field[j];
            }
        }
        for (int i = 0; i < _metric.Length; i++)
        {
            _metric[i] = (_metric[i] < 0) ? Math.Abs(_metric[i]) : 0;
        }
    }

    //// Compute the simple expected damage a bomb would score
    //private static void GetBlastPotential()
    //{
    //    _metric = new int[_field.Length];
    //    for (int i = 0; i < _field.Length; i++)
    //    {
    //        _metric[i] = (_field[i] > 0) ? 1 : 0;
    //        List<int> neighbours = GetNeighbours(i);
    //        if (neighbours != null)
    //        {
    //            foreach (int j in neighbours) _metric[i] += (_field[j] > 0) ? 1 : 0;
    //        }
    //    }            
    //}

    // Update the battle field upon dropping a bomb
    private static void Bomb(int cell)
    {
        List<int> neighbours = GetNeighbours(cell);
        foreach (int i in neighbours)
        {
            if (_field[i] > 0) _field[i]--;
        }
    }

    // Find the best bombing site - just return index of local maxima
    private static int FindBestBombingSite()
    {
        int max_idx = 0;
        int max_val = int.MinValue;
        for (int i = 0; i < _metric.Length; i++)
        {
            if (_metric[i] > max_val)
            {
                max_val = _metric[i];
                max_idx = i;
            }
        }
        return max_idx;
    }

    // Display the battle field on the console
    private static void PrintField(int cell)
    {
        for (int x = 0; x < _width; x++)
        {
            for (int y = 0; y < _length; y++)
            {
                int c = Get1DCoord(x, y);
                if (c == cell)
                    Console.Out.Write(string.Format("[{0}]", _field[c]).PadLeft(4));
                else
                    Console.Out.Write(string.Format(" {0} ", _field[c]).PadLeft(4));
            }
            Console.Out.Write(" || ");
            for (int y = 0; y < _length; y++)
            {
                int c = Get1DCoord(x, y);
                if (c == cell)
                    Console.Out.Write(string.Format("[{0}]", _metric[c]).PadLeft(4));
                else
                    Console.Out.Write(string.Format(" {0} ", _metric[c]).PadLeft(4));
            }
            Console.Out.WriteLine();
        }
        Console.Out.WriteLine();
    }           
  }
}

Ortaya çıkan bombalama paterni aşağıdaki gibi çıkarılır (soldaki alan değerleri, sağdaki metrik)

Round 1:
  2   1   4   2   3   2   6  ||   7  16   8  10   4  18   6
  3   5   3   1   1   1   9  ||  11  18  18  21  17  28   5
  4  [2]  4   2   3   4   1  ||  19 [32] 21  20  17  24  22
  7   6   2   4   4   3   6  ||   8  17  20  14  16  22   8
  1   2   1   1   1   2   4  ||  14  15  14  11  13  16   7

Round 2:
  2   1   4   2   3   2   6  ||   5  13   6   9   4  18   6
  2   4   2   1   1  [1]  9  ||  10  15  17  19  17 [28]  5
  3   2   3   2   3   4   1  ||  16  24  18  17  17  24  22
  6   5   1   4   4   3   6  ||   7  14  19  12  16  22   8
  1   2   1   1   1   2   4  ||  12  12  12  10  13  16   7

Round 3:
  2   1   4   2   2   1   5  ||   5  13   6   7   3  15   5
  2   4   2   1   0   1   8  ||  10  15  17  16  14  20   2
  3  [2]  3   2   2   3   0  ||  16 [24] 18  15  16  21  21
  6   5   1   4   4   3   6  ||   7  14  19  11  14  19   6
  1   2   1   1   1   2   4  ||  12  12  12  10  13  16   7

Round 4:
  2   1   4   2   2   1   5  ||   3  10   4   6   3  15   5
  1   3   1   1   0   1   8  ||   9  12  16  14  14  20   2
  2   2   2   2   2  [3]  0  ||  13  16  15  12  16 [21] 21
  5   4   0   4   4   3   6  ||   6  11  18   9  14  19   6
  1   2   1   1   1   2   4  ||  10   9  10   9  13  16   7

Round 5:
  2   1   4   2   2   1   5  ||   3  10   4   6   2  13   3
  1   3   1   1   0  [0]  7  ||   9  12  16  13  12 [19]  2
  2   2   2   2   1   3   0  ||  13  16  15  10  14  15  17
  5   4   0   4   3   2   5  ||   6  11  18   7  13  17   6
  1   2   1   1   1   2   4  ||  10   9  10   8  11  13   5

Round 6:
  2   1   4   2   1   0   4  ||   3  10   4   5   2  11   2
  1   3   1   1   0   0   6  ||   9  12  16  11   8  13   0
  2   2   2   2   0   2   0  ||  13  16  15   9  14  14  15
  5   4  [0]  4   3   2   5  ||   6  11 [18]  6  11  15   5
  1   2   1   1   1   2   4  ||  10   9  10   8  11  13   5

Round 7:
  2   1   4   2   1   0   4  ||   3  10   4   5   2  11   2
  1   3   1   1   0   0   6  ||   8  10  13   9   7  13   0
  2  [1]  1   1   0   2   0  ||  11 [15] 12   8  12  14  15
  5   3   0   3   3   2   5  ||   3   8  10   3   8  15   5
  1   1   0   0   1   2   4  ||   8   8   7   7   9  13   5

Round 8:
  2   1   4   2   1   0   4  ||   1   7   2   4   2  11   2
  0   2   0   1   0   0   6  ||   7   7  12   7   7  13   0
  1   1   0   1   0   2   0  ||   8   8  10   6  12  14  15
  4   2   0   3   3  [2]  5  ||   2   6   8   2   8 [15]  5
  1   1   0   0   1   2   4  ||   6   6   6   7   9  13   5

Round 9:
  2   1   4   2   1   0   4  ||   1   7   2   4   2  11   2
  0   2   0   1   0   0   6  ||   7   7  12   7   6  12   0
  1   1   0   1   0  [1]  0  ||   8   8  10   5  10 [13] 13
  4   2   0   3   2   2   4  ||   2   6   8   0   6   9   3
  1   1   0   0   0   1   3  ||   6   6   6   5   8  10   4

Round 10:
  2   1   4   2   1   0   4  ||   1   7   2   4   2  10   1
  0   2  [0]  1   0   0   5  ||   7   7 [12]  7   6  11   0
  1   1   0   1   0   1   0  ||   8   8  10   4   8   9  10
  4   2   0   3   1   1   3  ||   2   6   8   0   6   8   3
  1   1   0   0   0   1   3  ||   6   6   6   4   6   7   2

Round 11:
  2   0   3   1   1   0   4  ||   0   6   0   3   0  10   1
  0   1   0   0   0  [0]  5  ||   4   5   5   5   3 [11]  0
  1   0   0   0   0   1   0  ||   6   8   6   4   6   9  10
  4   2   0   3   1   1   3  ||   1   5   6   0   5   8   3
  1   1   0   0   0   1   3  ||   6   6   6   4   6   7   2

Round 12:
  2   0   3   1   0   0   3  ||   0   6   0   2   1   7   1
  0   1   0   0   0   0   4  ||   4   5   5   4   1   7   0
  1   0   0   0   0  [0]  0  ||   6   8   6   4   5  [9]  8
  4   2   0   3   1   1   3  ||   1   5   6   0   4   7   2
  1   1   0   0   0   1   3  ||   6   6   6   4   6   7   2

Round 13:
  2   0   3   1   0   0   3  ||   0   6   0   2   1   6   0
  0   1   0   0   0   0   3  ||   4   5   5   4   1   6   0
  1  [0]  0   0   0   0   0  ||   6  [8]  6   3   3   5   5
  4   2   0   3   0   0   2  ||   1   5   6   0   4   6   2
  1   1   0   0   0   1   3  ||   6   6   6   3   4   4   0

Round 14:
  2   0   3   1   0  [0]  3  ||   0   5   0   2   1  [6]  0
  0   0   0   0   0   0   3  ||   2   5   4   4   1   6   0
  0   0   0   0   0   0   0  ||   4   4   4   3   3   5   5
  3   1   0   3   0   0   2  ||   0   4   5   0   4   6   2
  1   1   0   0   0   1   3  ||   4   4   5   3   4   4   0

Round 15:
  2   0   3   1   0   0   2  ||   0   5   0   2   1   4   0
  0   0   0   0   0   0   2  ||   2   5   4   4   1   4   0
  0   0   0   0   0   0   0  ||   4   4   4   3   3   4   4
  3   1   0   3   0  [0]  2  ||   0   4   5   0   4  [6]  2
  1   1   0   0   0   1   3  ||   4   4   5   3   4   4   0

Round 16:
  2  [0]  3   1   0   0   2  ||   0  [5]  0   2   1   4   0
  0   0   0   0   0   0   2  ||   2   5   4   4   1   4   0
  0   0   0   0   0   0   0  ||   4   4   4   3   3   3   3
  3   1   0   3   0   0   1  ||   0   4   5   0   3   3   1
  1   1   0   0   0   0   2  ||   4   4   5   3   3   3   0

Round 17:
  1   0   2   1   0   0   2  ||   0   3   0   1   1   4   0
  0   0   0   0   0   0   2  ||   1   3   3   3   1   4   0
  0   0   0   0   0   0   0  ||   4   4   4   3   3   3   3
  3   1  [0]  3   0   0   1  ||   0   4  [5]  0   3   3   1
  1   1   0   0   0   0   2  ||   4   4   5   3   3   3   0

Round 18:
  1   0   2   1   0   0   2  ||   0   3   0   1   1   4   0
  0   0   0   0   0   0   2  ||   1   3   3   3   1   4   0
  0   0   0   0   0   0   0  ||   3   3   2   2   2   3   3
  3  [0]  0   2   0   0   1  ||   0  [4]  2   0   2   3   1
  1   0   0   0   0   0   2  ||   2   4   2   2   2   3   0

Round 19:
  1   0   2   1   0  [0]  2  ||   0   3   0   1   1  [4]  0
  0   0   0   0   0   0   2  ||   1   3   3   3   1   4   0
  0   0   0   0   0   0   0  ||   2   2   2   2   2   3   3
  2   0   0   2   0   0   1  ||   0   2   2   0   2   3   1
  0   0   0   0   0   0   2  ||   2   2   2   2   2   3   0

Round 20:
  1  [0]  2   1   0   0   1  ||   0  [3]  0   1   1   2   0
  0   0   0   0   0   0   1  ||   1   3   3   3   1   2   0
  0   0   0   0   0   0   0  ||   2   2   2   2   2   2   2
  2   0   0   2   0   0   1  ||   0   2   2   0   2   3   1
  0   0   0   0   0   0   2  ||   2   2   2   2   2   3   0

Round 21:
  0   0   1   1   0   0   1  ||   0   1   0   0   1   2   0
  0   0   0   0   0   0   1  ||   0   1   2   2   1   2   0
  0   0   0   0   0   0   0  ||   2   2   2   2   2   2   2
  2   0   0   2   0  [0]  1  ||   0   2   2   0   2  [3]  1
  0   0   0   0   0   0   2  ||   2   2   2   2   2   3   0

Round 22:
  0   0   1   1   0   0   1  ||   0   1   0   0   1   2   0
  0   0   0   0   0   0   1  ||   0   1   2   2   1   2   0
 [0]  0   0   0   0   0   0  ||  [2]  2   2   2   2   1   1
  2   0   0   2   0   0   0  ||   0   2   2   0   2   1   1
  0   0   0   0   0   0   1  ||   2   2   2   2   2   1   0

Round 23:
  0   0   1   1   0   0   1  ||   0   1   0   0   1   2   0
  0   0  [0]  0   0   0   1  ||   0   1  [2]  2   1   2   0
  0   0   0   0   0   0   0  ||   1   1   2   2   2   1   1
  1   0   0   2   0   0   0  ||   0   1   2   0   2   1   1
  0   0   0   0   0   0   1  ||   1   1   2   2   2   1   0

Round 24:
  0   0   0   0   0   0   1  ||   0   0   0   0   0   2   0
  0   0   0   0   0   0   1  ||   0   0   0   0   0   2   0
  0   0  [0]  0   0   0   0  ||   1   1  [2]  2   2   1   1
  1   0   0   2   0   0   0  ||   0   1   2   0   2   1   1
  0   0   0   0   0   0   1  ||   1   1   2   2   2   1   0

Round 25:
  0   0   0   0   0  [0]  1  ||   0   0   0   0   0  [2]  0
  0   0   0   0   0   0   1  ||   0   0   0   0   0   2   0
  0   0   0   0   0   0   0  ||   1   1   1   1   1   1   1
  1   0   0   1   0   0   0  ||   0   1   1   0   1   1   1
  0   0   0   0   0   0   1  ||   1   1   1   1   1   1   0

Round 26:
  0   0   0   0   0   0   0  ||   0   0   0   0   0   0   0
  0   0   0   0   0   0   0  ||   0   0   0   0   0   0   0
 [0]  0   0   0   0   0   0  ||  [1]  1   1   1   1   0   0
  1   0   0   1   0   0   0  ||   0   1   1   0   1   1   1
  0   0   0   0   0   0   1  ||   1   1   1   1   1   1   0

Round 27:
  0   0   0   0   0   0   0  ||   0   0   0   0   0   0   0
  0   0   0   0   0   0   0  ||   0   0   0   0   0   0   0
  0   0  [0]  0   0   0   0  ||   0   0  [1]  1   1   0   0
  0   0   0   1   0   0   0  ||   0   0   1   0   1   1   1
  0   0   0   0   0   0   1  ||   0   0   1   1   1   1   0

Round 28:
  0   0   0   0   0   0   0  ||   0   0   0   0   0   0   0
  0   0   0   0   0   0   0  ||   0   0   0   0   0   0   0
  0   0   0   0   0   0   0  ||   0   0   0   0   0   0   0
  0   0   0   0   0  [0]  0  ||   0   0   0   0   0  [1]  1
  0   0   0   0   0   0   1  ||   0   0   0   0   0   1   0

Done in 28 rounds

1

Bu, O (3 ^ (n)) derinlikli bir ağaç kullanılarak çözülebilir. Burada n, tüm karelerin toplamıdır.

Öncelikle, sorunu bir O (9 ^ n) ağacı ile çözmenin önemsiz olduğunu düşünün, olası tüm bombalama yerlerini düşünün. Bir örnek için Alfe'nin uygulanmasına bakınız .

Daha sonra, aşağıdan yukarıya bombalamak için çalışabileceğimizi ve yine de minimum bombalama paterni elde edebileceğimizi anlayın.

  1. Sol alt köşeden başlayın.
  2. Mantıklı olan tek oyunla (yukarı ve sağa) unutmaya bombalayın.
  3. Bir kare sağa git
  4. Hedef sıfırdan büyük bir değere sahip olsa da, mantıklı (doğrudan yukarı veya yukarı ve sağa doğru), hedefin değerini bir azaltarak ve her olasılık için yeni bir dal oluşturan 2 oyunun her birini düşünün.
  5. Başka birini sağa hareket ettirin.
  6. Hedef sıfırdan büyük bir değere sahip olsa da, anlamlı (sol, yukarı ve yukarı sağ) 3 oyunun her birini düşünün, hedefin değerini bir azaltın ve her olasılık için yeni bir dal yapın.
  7. Satır ortadan kalkana kadar 5. ve 6. adımları tekrarlayın.
  8. Bir satır yukarı gidin ve bulmaca çözülene kadar 1'den 7'ye kadar olan adımları tekrarlayın.

Bu algoritma doğru çünkü

  1. Her satırı bir noktada tamamlamak gerekir.
  2. Bir satırın tamamlanması için her zaman bir üst, bir alt veya bir satır içinde bir oynatma gerekir.
  3. Temizlenmemiş en düşük satırın üstündeki bir oyunu, sıradaki veya sıra altındaki bir oyundan seçmek her zaman iyi veya daha iyidir.

Uygulamada, bu algoritma düzenli olarak teorik maksimumundan daha iyi sonuç verecektir çünkü düzenli olarak komşuları bombalayacak ve aramanın boyutunu azaltacaktır. Her bombalamanın 4 ek hedefin değerini azalttığını varsayarsak, algoritmamız O (3 ^ (n / 4)) veya yaklaşık O (1,3 ^ n) olarak çalışacaktır.

Bu algoritma hala üstel olduğundan, aramanın derinliğini sınırlamak akıllıca olacaktır. İzin verilen dal sayısını bir sayı olan X ile sınırlayabiliriz ve bu derin olduktan sonra algoritmayı şimdiye kadar tanımladığı en iyi yolu seçmeye zorlarız (terminal yapraklarından birinde minimum toplam pano toplamına sahip olanı) ). O zaman algoritmamızın O (3 ^ X) zamanında çalışması garanti edilir, ancak doğru cevabı alması garanti edilmez. Bununla birlikte, artan hesaplama ve daha iyi cevaplar arasındaki ticaretin değerli olup olmadığını her zaman X'i artırabilir ve deneysel olarak test edebiliriz.


1

değerlendirme fonksiyonu, toplam:

int f (int ** matrix, int width, int height, int x, int y)
{
    int m[3][3] = { 0 };

    m[1][1] = matrix[x][y];
    if (x > 0) m[0][1] = matrix[x-1][y];
    if (x < width-1) m[2][1] = matrix[x+1][y];

    if (y > 0)
    {
        m[1][0] = matrix[x][y-1];
        if (x > 0) m[0][0] = matrix[x-1][y-1];
        if (x < width-1) m[2][0] = matrix[x+1][y-1];
    }

    if (y < height-1)
    {
        m[1][2] = matrix[x][y+1];
        if (x > 0) m[0][2] = matrix[x-1][y+1];
        if (x < width-1) m[2][2] = matrix[x+1][y+1];
    }

    return m[0][0]+m[0][1]+m[0][2]+m[1][0]+m[1][1]+m[1][2]+m[2][0]+m[2][1]+m[2][2];
}

amaç fonksiyonu:

Point bestState (int ** matrix, int width, int height)
{
    Point p = new Point(0,0);
    int bestScore = 0;
    int b = 0;

    for (int i=0; i<width; i++)
        for (int j=0; j<height; j++)
        {
            b = f(matrix,width,height,i,j);

            if (b > bestScore)
            {
                bestScore = best;
                p = new Point(i,j);
            }
        }

    retunr p;
}

yok fonksiyonu:

void destroy (int ** matrix, int width, int height, Point p)
{
    int x = p.x;
    int y = p.y;

    if(matrix[x][y] > 0) matrix[x][y]--;
    if (x > 0) if(matrix[x-1][y] > 0) matrix[x-1][y]--;
    if (x < width-1) if(matrix[x+1][y] > 0) matrix[x+1][y]--;

    if (y > 0)
    {
        if(matrix[x][y-1] > 0) matrix[x][y-1]--;
        if (x > 0) if(matrix[x-1][y-1] > 0) matrix[x-1][y-1]--;
        if (x < width-1) if(matrix[x+1][y-1] > 0) matrix[x+1][y-1]--;
    }

    if (y < height-1)
    {
        if(matrix[x][y] > 0) matrix[x][y+1]--;
        if (x > 0) if(matrix[x-1][y+1] > 0) matrix[x-1][y+1]--;
        if (x < width-1) if(matrix[x+1][y+1] > 0) matrix[x+1][y+1]--;
    }
}

hedef fonksiyonu:

bool isGoal (int ** matrix, int width, int height)
{
    for (int i=0; i<width; i++)
        for (int j=0; j<height; j++)
            if (matrix[i][j] > 0)
                return false;
    return true;
}

doğrusal maksimizasyon fonksiyonu:

void solve (int ** matrix, int width, int height)
{
    while (!isGoal(matrix,width,height))
    {
        destroy(matrix,width,height, bestState(matrix,width,height));
    }
}

Bu optimal değildir, ancak daha iyi bir değerlendirme fonksiyonu bularak optimize edilebilir.

.. ama bu problemi düşünürken, ana sorunlardan birinin bir noktada sıfırların ortasında terk edilmiş figürler almak olduğunu düşünüyordum, bu yüzden başka bir yaklaşımı ele alacağım. Mümkün olan en düşük sıfır değer (ler) in genel minimizasyonuna yol açan mümkün olduğunca sıfırlar


0

Tüm bu sorun bir düzenleme mesafesi hesaplamak için kaynar. Verili matris ile sıfır matris arasındaki Levenshtein mesafesinin bir varyantını hesaplayın; burada, ara diziler arasındaki mesafeleri saklamak için dinamik programlama kullanarak düzenlemelerin bombardımanlarla değiştirilir. Matrislerin bir karma değerini anahtar olarak kullanmanızı öneririm. Yalancı Python'da:

memo = {}

def bomb(matrix,i,j):
    # bomb matrix at i,j

def bombsRequired(matrix,i,j):
    # bombs required to zero matrix[i,j]

def distance(m1, i, len1, m2, j, len2):
    key = hash(m1)
    if memo[key] != None: 
        return memo[key]

    if len1 == 0: return len2
    if len2 == 0: return len1

    cost = 0
    if m1 != m2: cost = m1[i,j]
    m = bomb(m1,i,j)
    dist = distance(str1,i+1,len1-1,str2,j+1,len2-1)+cost)
    memo[key] = dist
    return dist

0

Bu ilk sorulan soruya bir cevaptı. Parametreleri değiştirdiğini fark etmemiştim.

Tüm hedeflerin bir listesini oluşturun. Bir düşüşten (kendisinin ve tüm komşularının) etkilenen pozitif değerlerin sayısına göre hedefe bir değer atayın. En yüksek değer dokuz olur.

Hedefleri, etkilenen hedeflerin sayısına göre (Azalan), etkilenen her hedefin toplamında ikincil bir azalan sıralama ile sıralayın.

En üst sıradaki hedefe bomba atın, ardından hedefleri yeniden hesaplayın ve tüm hedef değerler sıfır oluncaya kadar tekrarlayın.

Anlaşılan, bu her zaman en uygun değildir. Örneğin,

100011
011100
011100
011100
000000
100011

Bu yaklaşımın temizlenmesi 5 bomba alacaktı. Bununla birlikte, optimal olarak, bunu 4'te yapabilirsiniz. Yine de, oldukça darn yakın ve hiçbir geri izleme yoktur. Çoğu durumda, optimal veya çok yakın olacaktır.

Orijinal sorun sayılarını kullanarak, bu yaklaşım 28 bomba ile çözülür.

Bu yaklaşımı göstermek için kod ekleme (düğmeli bir form kullanarak):

         private void button1_Click(object sender, EventArgs e)
    {
        int[,] matrix = new int[10, 10] {{5, 20, 7, 1, 9, 8, 19, 16, 11, 3}, 
                                         {17, 8, 15, 17, 12, 4, 5, 16, 8, 18},
                                         { 4, 19, 12, 11, 9, 7, 4, 15, 14, 6},
                                         { 17, 20, 4, 9, 19, 8, 17, 2, 10, 8},
                                         { 3, 9, 10, 13, 8, 9, 12, 12, 6, 18}, 
                                         {16, 16, 2, 10, 7, 12, 17, 11, 4, 15},
                                         { 11, 1, 15, 1, 5, 11, 3, 12, 8, 3},
                                         { 7, 11, 16, 19, 17, 11, 20, 2, 5, 19},
                                         { 5, 18, 2, 17, 7, 14, 19, 11, 1, 6},
                                         { 13, 20, 8, 4, 15, 10, 19, 5, 11, 12}};


        int value = 0;
        List<Target> Targets = GetTargets(matrix);
        while (Targets.Count > 0)
        {
            BombTarget(ref matrix, Targets[0]);
            value += 1;
            Targets = GetTargets(matrix);
        }
        Console.WriteLine( value);
        MessageBox.Show("done: " + value);
    }

    private static void BombTarget(ref int[,] matrix, Target t)
    {
        for (int a = t.x - 1; a <= t.x + 1; a++)
        {
            for (int b = t.y - 1; b <= t.y + 1; b++)
            {
                if (a >= 0 && a <= matrix.GetUpperBound(0))
                {
                    if (b >= 0 && b <= matrix.GetUpperBound(1))
                    {
                        if (matrix[a, b] > 0)
                        {
                            matrix[a, b] -= 1;
                        }
                    }
                }
            }
        }
        Console.WriteLine("Dropped bomb on " + t.x + "," + t.y);
    }

    private static List<Target> GetTargets(int[,] matrix)
    {
        List<Target> Targets = new List<Target>();
        int width = matrix.GetUpperBound(0);
        int height = matrix.GetUpperBound(1);
        for (int x = 0; x <= width; x++)
        {
            for (int y = 0; y <= height; y++)
            {
                Target t = new Target();
                t.x = x;
                t.y = y;
                SetTargetValue(matrix, ref t);
                if (t.value > 0) Targets.Add(t);
            }
        }
        Targets = Targets.OrderByDescending(x => x.value).ThenByDescending( x => x.sum).ToList();
        return Targets;
    }

    private static void SetTargetValue(int[,] matrix, ref Target t)
    {
        for (int a = t.x - 1; a <= t.x + 1; a++)
        {
            for (int b = t.y - 1; b <= t.y + 1; b++)
            {
                if (a >= 0 && a <= matrix.GetUpperBound(0))
                {
                    if (b >= 0 && b <= matrix.GetUpperBound(1))
                    {
                        if (matrix[ a, b] > 0)
                        {
                            t.value += 1;
                            t.sum += matrix[a,b];
                        }

                    }
                }
            }
        }

    }

İhtiyacınız olacak bir sınıf:

        class Target
    {
        public int value;
        public int sum;
        public int x;
        public int y;
    }

1
Uygun değil. Karşı örnek: 09090Bu yaklaşım 18 bomba gerektirir. 9'da yapılabilir.
Mistik

@Mysticial Cevabı tam olarak okumadınız. Etkilenen sıfır olmayan alanların sayısına dayandığından, bu algoritma orta sıfırı bombalayacak ve 9 damla halinde yapılacaktı. 9'un yüksek değeri, sekiz komşu ve kendisinin olmasıdır.
Anthony Queen

O halde neden 1010101, 0010100?
Gizemli

@Mysticial İlk, İlk sıfır, sonra son sıfır vurulacaktı. İki damla olurdu. Çünkü her bomba attığında, bir sonraki en iyi hedefi yeniden hesaplar. Son örnek için yine orta sıfır; Bir damla.
Anthony Queen

1
@AnthonyQueen: bu işe yaramıyor. Karşı örnek için lütfen chat.stackoverflow.com/transcript/message/8224273#8224273 adresine bakın .
nneonneo
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.