_BlondieGirl_ Posted April 28, 2009 at 04:20 AM Report #259638 Posted April 28, 2009 at 04:20 AM Boa noite, meninos. 🙂 Trata-se do seguinte: tenho andado às voltas com um projecto e não acho uma forma eficiente de o resolver. O enunciado é basicamente isto: Um ladrão precisa de chegar ao tesouro, mas para conseguir isso, precisa de explodir com certas paredes, com determinada dureza, sendo que estas, protegem o tesouro. O objectivo é determinar a quantidade mínima de explosivos necessária para o ladrão chegar ao tesouro. Notas: O ladrão parte da posição (1,1), canto superior esquerdo.Não é possível explodir paredes "na diagonal".Não é possível explodir 2 paredes de seguida. O input é dado da seguinte forma: nº linhas nº colunas (x, y) <--- posição do tesouro nº paredes (x1, y1, dureza1) (x2, y2, dureza2) .... (xn, yn, durezan) <-- posição de um quadrado de parede e a sua dureza. Exemplo: 3 3 (dimensão) 5 5 (tesouro) 9 (nº paredes) 2 2 3 2 3 3 2 4 3 3 1 1 3 2 1 3 4 3 4 2 3 4 3 3 4 4 2 Sendo o resultado gráfico, isto: http://img15.imageshack.us/img15/4927/capturaecra1i.png Solução = 3 Porquê? Embora fosse mais barato partir as duas paredes de "1", tal não é possível (ver notas). Também não é possível partir paredes na diagonal, sendo que a parede "2" fica excluída. Restam as paredes "3" e essa é a solução. O QUE É PRECISO FAZER Pensar! Ah pois... mas isto é alto mindfuck! Porque é possível descobrir isto facilmente "à bruta", mas o meu sistema de submissão (Mooshak) obviamente que limite a stack a 32MB, penso eu, ou qualquer coisa mesmo muito fraquinha. A minha ideia seria ir parsando o input e construíndo conjuntos de "espaços vazios", e cada conjunto ser um nó e os arcos seriam a dureza mínima das paredes que dividem esses conjuntos. Bastaria no fim, aplicar um Dikstra para achar o caminho mais curto do nó do ladrão ao nó do tesouro. Vamos lá ao que interessa: Podem ver a posição do ladrão e do tesouro e as durezas de cada parede. http://img6.imageshack.us/img6/930/asa1.png A ideia é preencher a estrutura à medida que se lê o input, marcando-se cada espaço vazio com o nome do seu conjunto. Isto faz-se sempre percorrendo (linhas x colunas) quadrados... 2 for, é feio pois é, mas desta forma conseguimos o seguinte esquema: http://img6.imageshack.us/img6/4884/asa2b.png Em que cada letra representa um conjunto. A laranja estão realçadas as paredes menos duras que permitem o acesso de um conjunto a outro. A ideia é portanto, tornar cada conjunto num nó, e cada parede de acesso é um arco, com peso igual à sua dureza! http://img16.imageshack.us/img16/8618/asa3.png Como podem ver, a solução passa por fazer um Dikstra ao nó A e calcular o caminho mais curto até ao nó E! Percebem? :roll: E de que forma preencho isto em código? Utilizando uma mega matriz, ora pois: actual = coordenadas actuais (x,y) input = coordenadas do prox input à_esquerda = (x, y-1) <--- posição à esquerda por cima = (x-1, y) <---- posição em cima for x = 1 until nº linhas { for y =1 until nº colunas { if ( posicao(actual) == posicao(input) ) posicao(actual) = peso(input); else { if ( posicao(por_cima) IS conjunto && posicao(à_esquerda) IS conjunto && conjunto(por_cima) != conjunto(à_esquerda) ) conjunto(actual) = conjunto(por_cima); // fazer um for aqui e andar para trás preenchendo cada espaço com o conjunto(por_cima); // sair do else if( posicao(à_esquerda) IS conjunto) conjunto(actual) = conjunto(à_esquerda); // sair do else if( posicao(por_cima) IS conjunto) conjunto(actual) = conjunto(por_cima); // sair do else } // quando se chega aqui, vamos analisar outro quadrado. } } Desculpem o pseudocódigo manhoso, mas espero que percebam a ideia. PORQUE É QUE ISTO É PÉSSIMO? O máximo de linhas e colunas é 10^6, o que significa 10^12 posições. Além de extremamente lento, memória para isto... só para alocar um só inteiro, são precisos quase 40 MB. A ajuda que eu pedia a vocês, meus caros geeks da algoritmia, era que me ajudassem numa forma de parsar o input e construir o grafo dos conjuntos sem parede, para eu depois aplicar o Dikstra! Já ouvi falar/pensei em guardar apenas os limites de cada conjunto e fazer contas de intersecção de conjuntos, já ouvi falar em buffers temporários de algumas linhas, blá blá, muito bonito, mas ando na mesma! (entregar dia 30 de Abril) Quero reforçar aqui a ideia de que não ando a pedinchar solução nenhuma, apenas precisava, se pudessem, de umas luzes para iluminar esta pobre cabeça universitária. O meu obrigada (1º post 😛 ) Enunciado: http://pdfvia.com/showfile-1508/proj2.pdf Adenda: http://pdfvia.com/showfile-1509/adenda.pdf
mogers Posted April 28, 2009 at 09:33 AM Report #259652 Posted April 28, 2009 at 09:33 AM Olá, Ainda não tive tempo de ler tudo, mas queria saber o seguinte: Que algoritmos de grafos conheces? (para saber o que é que já dominas) Qual é a linguagem de programação que estás a usar? edit: 5 5 (tesouro) 3 3 (dimensão) deveria ser 5 5 (dimensão) 3 3 (tesouro) Preferia ver o enunciado original. Acho que estás um pouco confusa e a forma que descreveste não serve para o problema. Qual é o limite para o número de paredes? "What we do for ourselves dies with us. What we do for others and the world, remains and is immortal.", Albert Pine Blog pessoal : contém alguns puzzles, algoritmos e problemas para se resolver com programação.
_BlondieGirl_ Posted April 28, 2009 at 03:14 PM Author Report #259715 Posted April 28, 2009 at 03:14 PM Olá, Ainda não tive tempo de ler tudo, mas queria saber o seguinte: Que algoritmos de grafos conheces? (para saber o que é que já dominas) Qual é a linguagem de programação que estás a usar? Preferia ver o enunciado original. Acho que estás um pouco confusa e a forma que descreveste não serve para o problema. Qual é o limite para o número de paredes? Isto é uma cadeira de Análise e Síntese de Algoritmos, até a semana passada tenho estado a dar toda a espécie de algoritmos em grafos: algoritmos elementares, caminhos mais curtos, fluxos máximos/mínimos, árvores abrangentes. Não te consigo dizer todos os algoritmos, porque foram imeeeensos: DFS, BFS, Tarjan, Boruvka, Floyd-Warshal, Ford-Fulkerson, Edmonds-Karp, Kruskal, Bellman-Ford, Dikstra, Johnson, Prim, ai tantos senhores, que eu sei lá. Agora já vou em Programação Linear, mas isso já não é chamado para aqui. Tinhas razão, alterei o meu post em cima. Tens aqui o enunciado e uma pequena adenda: Enunciado: http://pdfvia.com/showfile-1508/proj2.pdf Adenda: http://pdfvia.com/showfile-1509/adenda.pdf O nº máximo de paredes é, no limite, o caso da figura 3 da adenda, penso eu. Obrigada pela disponibilidade, desde já.
Pedro Ribeiro Posted April 28, 2009 at 05:48 PM Report #259750 Posted April 28, 2009 at 05:48 PM Por respeito ao Vasco Manquinho (professor no IST, penso que também dá ASA), não te posso responder, _BlondieGirl_. Isto porque sou "colega" dele (embora eu ainda seja aluno de doutoramento) mogers, se te "ajudar", podes ver a discussão no fórum do IST 😛 http://leic.estudante.com.pt/forum/viewtopic.php?f=38&t=4292
mogers Posted April 28, 2009 at 06:18 PM Report #259754 Posted April 28, 2009 at 06:18 PM Nesse forum falam no grafo dual que era o que tinha em mente também. Mas antes de o construir é preciso saber o limite máximo de paredes que não me parece nada que seja o daquele exemplo da adenda - edit: acho que percebi mal o que querias dizer. De qualquer modo um caso desses dava algo na ordem de 10^10 ou 10^11 paredes e isto não é praticável. Eu ainda não pensei muito a sério por causa de não conhecer este limite. Acho que os limites do número de linhas e colunas não servem para nada. PS: os limites de N e M estão no mooshak? No enunciado e na adenda não vi nada sobre isso. "What we do for ourselves dies with us. What we do for others and the world, remains and is immortal.", Albert Pine Blog pessoal : contém alguns puzzles, algoritmos e problemas para se resolver com programação.
Warrior Posted April 28, 2009 at 07:17 PM Report #259767 Posted April 28, 2009 at 07:17 PM Qual é o limite de memória mesmo? No pior caso, o número de nós será n*m/2, com 4 arcos aproximadamente cada um (obviamente que nos extremos não temos 4 arcos). Assumindo que isto cabe em memória, podes perfeitamente usar o input a teu favor, visto vir ordenado por X e Y. Com um algoritmo de Union Find podes montar o grafo à medida que vais lendo o input, em algo como n*m*log(n). Vai dar um bocadinho de trabalho, mas acho que funciona. Edit: Se por acaso o teu professor colocar o Mooshak público depois do prazo de entrega deixa por aqui o link porque fiquei com vontade de o resolver.
mogers Posted April 28, 2009 at 08:10 PM Report #259781 Posted April 28, 2009 at 08:10 PM O máximo de linhas e colunas é 10^6, o que significa 10^12 posições. Leste mesmo na diagonal pah 😛 Eu tenho dúvidas sobre o uso do Union Find aqui por causa daquela restrição de não poderes furar paredes adjacentes. Depende do número de paredes... "What we do for ourselves dies with us. What we do for others and the world, remains and is immortal.", Albert Pine Blog pessoal : contém alguns puzzles, algoritmos e problemas para se resolver com programação.
Warrior Posted April 28, 2009 at 08:27 PM Report #259785 Posted April 28, 2009 at 08:27 PM Basta-te verificar se as paredes são adjacentes. A minha ideia envolvia ler "linha a linha", e tendo por base a linha anterior, ligar as células, e passar ao tratamento da próxima. Eu não perguntei o número de linhas, mas os limites de memória.
mogers Posted April 28, 2009 at 08:39 PM Report #259793 Posted April 28, 2009 at 08:39 PM Eu sei, mas manténs em algo como n*m*log(n). isto?? com aqueles limites é impensável... "What we do for ourselves dies with us. What we do for others and the world, remains and is immortal.", Albert Pine Blog pessoal : contém alguns puzzles, algoritmos e problemas para se resolver com programação.
Warrior Posted April 28, 2009 at 08:59 PM Report #259814 Posted April 28, 2009 at 08:59 PM não preciso disso, basta K*log(n) em que K é o número de paredes. O pior caso é n*m/2, mas isso nem dá para ler o input..
_BlondieGirl_ Posted April 28, 2009 at 09:13 PM Author Report #259824 Posted April 28, 2009 at 09:13 PM O limite para o 10^6 tanto para o M como para o N. Eu penso que o limite da memória seja 32MB. É que estamos meio desesperados, estamos quase na data de limite, e em 224 alunos, existem apenas 5 positivas (19, 15, 12, 11, 10) e 9 submissões com menos de 10... Saí agora de um teste de Compiladores, embora com laboratórios para fazer, posso-me dedicar mais a isto. Como alguém já falou, como o objectivo é ler o input e construir um grafo imediatamente, pensei no seguinte: 1 ou 2 buffers contendo linhas lidas anteriormente, um para a linha anterior e outro para a linha que estamos a ler e ir fazendo comparações na vizinhança de cada posição... a complexidade não deixa de ser O(N*M)... ainda não testei isto no papel a fundo. É o caminho mais viável, não acham? Vou pensar no assunto. Se alguma alma genial me puder aconselhar sobre como optimizar o processo, sou toda ouvidos! @Pedro Ribeiro Compreendo perfeitamente. 😉
mogers Posted April 28, 2009 at 09:25 PM Report #259834 Posted April 28, 2009 at 09:25 PM Essa solução talvez dê para positiva. Para mim, a chave para o problema é identificar os circuitos fechados de paredes como podes ver na primeira imagem do enunciado e construir um grafo entre as regiões fronteira destes circuitos, com peso igual à parede visinha de cada par de regiões com menor custo. Then dijkstra como já referiste: desde a região que contém 1,1 até à região que contém o tesouro. Para a construção do grafo está a parecer-me ser necessário O(P^2) mas não tenho bem a certeza e sabendo o limite já dava para dar uma indicação se a solução era viável ou não. Com estas ideias, para a solução óptima, os limites de N e M são irrelevantes porque o tempo de execução não depende destes. "What we do for ourselves dies with us. What we do for others and the world, remains and is immortal.", Albert Pine Blog pessoal : contém alguns puzzles, algoritmos e problemas para se resolver com programação.
Warrior Posted April 28, 2009 at 09:38 PM Report #259836 Posted April 28, 2009 at 09:38 PM 1 ou 2 buffers contendo linhas lidas anteriormente, um para a linha anterior e outro para a linha que estamos a ler e ir fazendo comparações na vizinhança de cada posição... a complexidade não deixa de ser O(N*M)... ainda não testei isto no papel a fundo. É o caminho mais viável, não acham? Não precisas de ir posição a posição. O truque está no facto que se sabes que numa determinada linha há uma parede na posição K, e outra na posição L, todas as células desde K até L pertencem ao mesmo nó do teu grafo, logo não precisas de estar a criar vários nós.
_BlondieGirl_ Posted April 28, 2009 at 11:18 PM Author Report #259855 Posted April 28, 2009 at 11:18 PM Não precisas de ir posição a posição. O truque está no facto que se sabes que numa determinada linha há uma parede na posição K, e outra na posição L, todas as células desde K até L pertencem ao mesmo nó do teu grafo, logo não precisas de estar a criar vários nós. Sim, mas entre essa parede em K e a outra em L, esses espaços "vazios" podem estar em contacto com outros conjuntos (diferentes até) da linha anterior, e ser preciso actualizá-los. Logo esse segmento sem paredes pertente a um conjunto sim... e qual? É necessário "olhar" para cima. Estou certa?
Warrior Posted April 29, 2009 at 02:40 PM Report #259960 Posted April 29, 2009 at 02:40 PM Exacto, só precisas de intersectar esse intervalo com todos os intervalos da linha de cima, e onde houver células em comum, juntas os conjuntos (ou seja, passa a ser um nó único). Julgo que podes achar esses intervalos em log n com uma espécie de pesquisa binária.
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now