2012-03-13 21 views
12

मैं स्थानीय प्रोग्रामिंग प्रतियोगिता से प्रोग्रामिंग समस्या की समीक्षा कर रहा हूं।प्रोग्रामिंग अभ्यास (फिटिंग पाइप) के लिए बैकट्रैकिंग समाधान

आप समस्या here (पीडीएफ) डाउनलोड कर सकते हैं। यह डच में है लेकिन चित्र इसे समझने में मदद करेंगे।

आपको कुछ पाइप और कुछ गायब धब्बे (प्रश्नचिह्न) के साथ इनपुट के रूप में एक एम * एम ग्रिड प्राप्त होता है। शेष पाइप को ग्रिड में रखा जाना चाहिए ताकि वे दूसरों के साथ जुड़ सकें।

प्रत्येक पाइप को एक पत्र के रूप में दर्शाया जाता है (पृष्ठ 2 पर चित्र देखें)। अक्षर 'ए' में मान 1 है, 'बी' का मान 2 है, ..

मैंने बैकट्रैकिंग के साथ इसे हल करने का प्रयास किया (यह अभी तक काफी काम नहीं करता है)। लेकिन चूंकि ग्रिड 10x10 हो सकता है यह बहुत धीमा हो जाएगा। क्या कोई बेहतर (तेज) समाधान/दृष्टिकोण सुझा सकता है?

#include <iostream> 
#include <stdio.h> 
#include <vector> 
using namespace std; 

#define sz(a) int((a).size()) 
#define pb push_back 

int m, found; 
string letters; 
vector<int> done; 
vector<string> a; 

int ok(char letter, int c, int r) 
{ 
    int val = letter - 'A' + 1; 

    //checking if no side goes outside 
    if (r == 0 && (val & 1)) 
     return 0; 
    if (r == m - 1 && (val & 4)) 
     return 0; 
    if (c == 0 && (val & 8)) 
     return 0; 
    if (c == m - 1 && (val & 2)) 
     return 0; 

    //check if the side is connected the other pipe on the grid 
    if (r > 0 && a[r - 1][c] != '?' && (a[r - 1][c] & 4) && !(val & 1)) 
     return 0; 
    if (c > 0 && a[r][c - 1] != '?' && (a[r][c - 1] & 2) && !(val & 8)) 
     return 0; 
    if (r < m - 1 && a[r + 1][c] != '?' && (a[r + 1][c] & 1) && !(val & 4)) 
     return 0; 
    if (c < m - 1 && a[r][c + 1] != '?' && (a[r][c + 1] & 8) && !(val & 2)) 
     return 0; 

    return 1; 
} 

void solve(int num_placed, int pos) 
{ 
    if (found) return; 

    //done 
    if (num_placed == sz(letters)) { 
     for (int i = 0; i < m; ++i) 
      cout << a[i] << endl; 
     found = 1; 
     return; 
    } 

    int c = pos % m; 
    int r = pos/m; 
    if (a[r][c] != '?') 
     solve(num_placed, pos + 1); 

    //try all the pipes on this position 
    for (int i = 0; i < sz(letters); ++i) { 
     if (!done[i] && ok(letters[i], c, r)) { 
      a[r][c] = letters[i]; 
      done[i] = 1; 
      solve(num_placed + 1, pos + 1); 
      done[i] = 0; 
      a[r][c] = '?'; 
     } 
    } 
} 

int main() 
{ 
    freopen("input.txt", "r", stdin); 

    int n; 
    cin >> n; 

    while (n--) { 
     cin >> m; 
     cin >> letters; 

     cout << m << endl; 
     a.clear(); 
     for (int i = 0; i < m; ++i) { 
      string line; 
      cin >> line; 
      a.pb(line); 
     } 

     done = vector<int>(sz(letters), 0); 

     found = 0; 
     solve(0, 0); 
    } 

    return 0; 
} 
+1

आपने इनपुट से लिंक किया है, समस्या का विवरण नहीं। – Bart

+2

समस्या का विवरण जैसा दिखता है: http://www.vlaamseprogrammeerwedstrijd.be/2011/opgaves/cat2-2011/loodgieter.pdf – fgb

+0

हाँ, आप सही हैं। मैंने यूआरएल बदल दिया। – Jasper

उत्तर

7

मूल जबाब

आप सभी अपने आप को कोड लिखने के लिए है या आप अन्य उपकरणों की खोज में रुचि रखते हैं हैं? क्योंकि मैं बाधा प्रचार/रैखिक प्रोग्रामिंग को देखने का सुझाव दूंगा। आपके पास पहले से ही बहुत सी सीमा बाधाएं हैं - बाहरी किनारों में पाइप, साथ ही आंतरिक किनारों नहीं हो सकते हैं - इसलिए मुझे लगता है कि यह काफी कुशलता से काम करेगा। भी, बाधाएं दिखती हैं कि वे साधारण समानताएं होंगी, इसलिए इसे स्थापित करना बहुत आसान होना चाहिए।

मेरे पास यहां अधिक जानकारी देने के लिए पर्याप्त अनुभव नहीं है (हालांकि अगर मेरे पास अगले सप्ताह में समय है तो मैं इसे किसी बिंदु पर जा सकता हूं), लेकिन अगर इस तरह की बात दिलचस्प है तो थोड़ा सा है another answer i wrote some time ago में अधिक पृष्ठभूमि।

ps दिलचस्प सवाल; इसे पोस्ट करने के लिए धन्यवाद।

[संपादित करें: यदि आप अन्य पुस्तकालयों का उपयोग नहीं कर सकते हैं तो आप बाधा प्रचार स्वयं कर सकते हैं। एक अद्भुत article by norvig है जो दिखाता है कि सुडोकू के लिए यह कैसे करें। मैं दृढ़ता से पढ़ने कि सुझाव है - मुझे लगता है कि आप कैसे भर में तकनीक ले जाने के लिए, भले ही यह सुडोकू और अजगर देखेंगे]

अद्यतन जबाब (2012-04-06 - ब्लॉग संदर्भ के साथ अद्यतन किया;। वर्ष टिप्पणियों थे गाड़ी)

गहराई-पहले खोज, जहां अगले खाली सेल प्रत्येक उपलब्ध, संगत टाइल से भर जाता है, और स्थिरता की जांच दोनों किनारे की कमी (किनारे से दूर नहीं पाइप) और निकटतम पड़ोसियों भी शामिल है, काफी कुशल है । मेरे पास क्लोजर में एक अप्रत्याशित कार्यान्वयन है जो छोटे उदाहरण को लगभग 0.4ms (जेएमवी गर्म करने के बाद 360ms में 1000) में हल करेगा और 3ms में बड़ा होगा (सेड्रिक वैन गोएथेम एक अनुकूलित के लिए 1ms की रिपोर्ट करता है - लेकिन फिर भी ओओ - जावा कार्यान्वयन, जो उचित लगता है)। यह 12s में 10x10 पहेली (ट्यूबों की केंद्रित चक्रों को प्रारंभिक संकेतों के साथ) हल कर सकता है।

मैंने एक "स्मार्ट" दृष्टिकोण को देखने में भी समय बिताया, जो प्रत्येक सेल पर बाधाओं को ट्रैक करता है, जो ऊपर नॉरविग के पेपर की तरह है। और फिर मैंने choco का उपयोग करने की कोशिश की। यह सब blog posts here में अधिक विस्तार से वर्णित है (मेरे पास इस उत्तर के अपडेट में अधिक जानकारी थी, लेकिन वे छोटी गाड़ी कोड पर आधारित थे - ब्लॉग में अधिक, बेहतर जानकारी है)। स्रोत डाउनलोड के लिए भी उपलब्ध है।

यह सब से सामान्य निष्कर्ष यह था कि प्रत्यक्ष खोज 10x10 तक ठीक है। इसके बाद, अधिक स्मारक मदद कर सकते हैं, लेकिन यह सुनिश्चित करना मुश्किल है क्योंकि परीक्षण के मामलों को उत्पन्न करना कठिन होता है (वे संदिग्ध होते हैं, भले ही बहुत से कोशिकाएं दी जाती हैं)।

+0

हां, यह उस समस्या श्रेणी में फिट बैठता है लेकिन मैं गैर-मानक पुस्तकालयों का उपयोग नहीं कर सकता – Jasper

+2

नॉरविग ने सुडोकू को हल करने पर एक बहुत अच्छा लेख लिखा है जो बताता है कि समर्पित पैकेज का उपयोग किये बिना अन्य बाधा प्रचार के साथ बैकट्रैकिंग खोज को कैसे जोड़ना है। फिलहाल आपके पास खोज है लेकिन अन्य बाधाओं का उपयोग नहीं कर रही है (जैसे सीमा पार करने वाली पाइप)। इसलिए मैं http://norvig.com/sudoku.html पढ़ने का सुझाव दूंगा जहां वह इस तरह की समस्या को हल करने के लिए बहुत पठनीय विवरण में वर्णन करता है। –

+0

धन्यवाद एंड्रयू, बहुत अच्छी तरह से लिखा है। यह Norvig के आलेख के लिंक के लिए – Jasper

2

अच्छी समस्या। मुझे ओ (एनएम 8^एम) में एक समाधान मिला है, जो कि पर्याप्त है।

  1. पहली और दूसरी पंक्ति के बीच सीमा पर फ़ोकस करें। 2^एम संभावनाएं हैं (आउटगोइंग लाइन या नहीं, प्रत्येक तरफ के लिए)। यह ऊपरी सीमा रेखा होगी, निचली सीमा रेखा प्रत्येक तरफ कोई कनेक्शन नहीं होगी।

  2. निचली सीमा रेखा और ऊपरी सीमा रेखा (जो 2^एम · 2^एम = 4^मीटर जोड़े) की प्रत्येक जोड़ी के लिए, प्रत्येक पंक्ति की गणना करने के लिए गणना करें। यदि आप बाईं ओर से आते हैं, तो आप हैं बाईं तरफ, शीर्ष तरफ और नीचे की तरफ दिया गया है, इसलिए आपके पास केवल 2 संभावनाएं हैं। यदि आप जिस टाइल को देखते हैं वह नक्शे में तय किया गया है, तो जांचें कि यह अन्यथा फिट बैठता है और निरस्त करता है। इसे बार-बार कॉल करें और आपको कुल 2^एम पंक्तियां या कुल मिलाकर 4^एम * 2^एम = 8^मीटर मिलती है।

  3. जबकि अंतिम चरण शुद्ध ब्रूट फोर्स था, हम इस बार डीपी का उपयोग करते हैं। सरणी की एक सरणी में टुपल (सीमा, ईंटों का इस्तेमाल, पंक्ति) सुरक्षित करें। सरणी [0] में एक एकल ट्यूपल (खाली सीमा, नो-ईंट-प्रयुक्त, कुछ नहीं) होगा। सरणी [एन] में पंक्ति में सभी 8^एम जेनरेट की गई पंक्तियां होती हैं (1 से शुरू होती हैं) सरणी में प्रत्येक आइटम के साथ संयुक्त [एन -1] यह फिट बैठती है (यानी आइटम की सीमा पंक्ति की निचली सीमा के समान होती है) ध्यान दें कि यदि आप चरण 2 में लूप के साथ इस स्थिति को स्मार्ट रूप से संयोजित करते हैं, तो इससे आपको कुछ भी लागत नहीं है।

  4. सभी tuples को हटाएं जिन्हें उपलब्ध से एक प्रकार की अधिक ईंटों की आवश्यकता है और सरणी को सॉर्ट करें। फिर चरण 2 पर जारी रखें जब तक कि सभी पंक्तियों को संभाला नहीं जाता है।

  5. यदि आप समाप्त कर चुके हैं, तो tuple (खाली सीमा, सभी ईंट-प्रयुक्त, पंक्ति) के लिए सरणी [n] में जांचें। चूंकि आपके कार्य विवरण का तात्पर्य है कि यह मौजूद है, इसकी पंक्ति मुद्रित करें। फिर पंक्ति की निचली सीमा को देखें और उसके लिए सरणी [n-1] में खोजें और इसे प्रिंट करें, और इसी तरह।

आशा है कि आप मेरी अंग्रेजी समझें।

+0

उत्तर देने के लिए Thx लेकिन मुझे डर है कि मैं आपके समाधान को समझ नहीं पा रहा हूं। डीपी द्वारा – Jasper

+0

वह गतिशील प्रोग्रामिंग का मतलब है। –