Você está na página 1de 7

1. O menor número possível de multiplicações escalares para obter a matriz M é 3400.

E a
melhor forma de multiplicá-las é: (A x ((B x C) x D)).

Abaixo temos a árvore de recursão para a resolução do problema para as quatro matrizes:

Números dentro dos círculos representam os índices de início e fim respectivamente. Círculos
verdes são os casos base, onde temos somente uma matriz, e, portanto, o custo de multiplicação é 0.
Círculos azuis são subproblemas que foram repetidos durante a execução do algoritmo.

Observe que 3 dos 9 subproblemas (aqui excluindo a execução dos casos base, pois têm tempo
constante) se repetiram, ou seja, aproximadamente 33% do trabalho (nesse caso) é repetido se não
for reaproveitado. Por isso é interessante utilizarmos a programação dinâmica nesse problema.

Aplicando a programação dinâmica, eliminamos a necessidade de calcular os casos repetidos,


através do uso da memorização. Podemos demonstrar isso graficamente com um DAG (Directed
Acyclic Graph, do inglês, Grafo Acíclico Direcionado), observe:
Note que os casos repetidos agora não mais existem, e subproblemas que requisitarem o
mesmo subproblema apontam para a mesma instância desse último.

Para finalizarmos, mostraremos a seguir o processo de construção da tabela de memorização


para conseguir atingir o objetivo de otimizar a resolução do problema. Resolvemos utilizando a
abordagem top-down. O processo de construção da tabela pode mudar conforme a abordagem
utilizada.

Como temos 4 matrizes, teremos uma tabela 4x4, onde a linha indica o índice de início e a
coluna o índice do fim. Optamos por inicializá-la com todos os valores em infinito.

0 1 2 3
0 infinity infinity infinity infinity
1 infinity infinity infinity infinity
2 infinity infinity infinity infinity
3 infinity infinity infinity infinity

Após a recursão atingir o último nível da árvore, obtemos o primeiro valor:

0 1 2 3
0 infinity infinity infinity infinity
1 infinity infinity infinity infinity
2 infinity infinity infinity 3000
3 infinity infinity infinity infinity

Que nos diz que para multiplicar as matrizes de 2 a 3, precisamos de 3000 multiplicações
escalares no mínimo. Observe que nosso objetivo é obter o menor número de multiplicações entre as
matrizes 0 e 3 (4 matrizes que nos foram dadas de entrada), então a resposta deverá estar contida no
canto superior direito após a execução do algoritmo, vejamos a construção da tabela:

0 1 2 3
0 infinity infinity infinity infinity
1 infinity infinity infinity infinity
2 infinity infinity infinity 3000
3 infinity infinity infinity infinity

0 1 2 3
0 infinity infinity infinity infinity
1 infinity infinity 1200 infinity
2 infinity infinity infinity 3000
3 infinity infinity infinity infinity
0 1 2 3
0 infinity infinity infinity infinity
1 infinity infinity 1200 1400
2 infinity infinity infinity 3000
3 infinity infinity infinity infinity

0 1 2 3
0 infinity 12000 infinity infinity
1 infinity infinity 1200 1400
2 infinity infinity infinity 3000
3 infinity infinity infinity infinity

0 1 2 3
0 infinity 12000 9200 infinity
1 infinity infinity 1200 1400
2 infinity infinity infinity 3000
3 infinity infinity infinity infinity

0 1 2 3
0 infinity 12000 9200 3400
1 infinity infinity 1200 1400
2 infinity infinity infinity 3000
3 infinity infinity infinity infinity

Nesse ponto achamos a resposta ótima para o problema original. As posições da tabela não
utilizadas são ocorrências dos casos base (i = j) ou casos impossíveis (i > j).
2. O fluxo máximo do grafo dado é 6. Abaixo executamos ilustradamente o algoritmo de Ford-
Fulkerson, que nos permitiu achar o fluxo máximo:

Iniciamos com o grafo dado, representamos a fonte por source e o terminal por sink:

Executar um DFS para achar um caminho até o terminal que não esteja cheio:

Diminuir a capacidade em 2 (gargalo) para cada uma das arestas do caminho encontrado e
aumentar a capacidade em 2 (gargalo) dos back-edges: (Soma dos gargalos é 2)
Executar um DFS para achar um caminho até o terminal que não esteja cheio:

Diminuir a capacidade em 2 nas arestas, e aumentar em 2 nos back-edges.

(Soma dos gargalos é 4).

Executar um DFS para achar um caminho até o terminal que não esteja cheio:

Diminuir a capacide em 2 nas arestas, e aumentar em 2 nos back-edges: (Soma dos gargalos é 6)
Executar um DFS para achar um caminho até o terminal que não esteja cheio:

Não é mais possível achar um caminho com capacidade sobrando, condição de parada do algoritmo.
O fluxo máximo é o valor que foi obtido somando os valores de gargalos, que nesse caso resultou em
6.

3. A seguir faremos a redução do Subset Sum para o problema Partition, antes de mais nada,

uma breve descrição desses dois problemas:

 Subset Sum: dado um conjunto S formado por inteiros e um número k, verificar se existe
um subconjunto de S com soma k.
 Partition: dado um conjunto S formado por inteiros, verificar se é possível dividir S em
dois subconjuntos S’ e S’’, de forma que a soma desses tenha o mesmo valor.

Obs: A função soma usada nos passos seguintes retorna o valor da soma de todos os
elementos de um conjunto.

Vamos resolver o problema Subset sum dado a nós como entrada um conjunto B, e a soma
desejada k.

Comecemos com o conjunto de inteiros S, tome t = soma(S).

Agora crie um conjunto A = { 2k – t }.

Definimos então S’ = S ∪ A.

Note que soma(S’) = soma(S) + soma(A), mais especialmente: soma(S’) = t + 2k - t = 2k.

Ou seja, agora temos um conjunto S’ que tem soma total equivale ao dobro do valor que
gostaríamos de verificar se é soma de algum subconjunto de S.

Agora, chame Partition, com a entrada S’. Se Partition retornar verdadeiro, a resposta para o
problema original é verdadeiro também. A mesma coisa vale para os casos falsos.

Isso é fácil de mostrar: lembre que Partition nos diz se é possível dividir um conjunto em dois
subconjuntos, tal que a soma desses últimos sejam iguais.

Ora, o conjunto S’ tem soma 2k, se existe uma divisão tal que a soma seja igual, então cada
um dos subconjuntos obtidos por Portition obrigatoriamente tem soma k. Mas note que ter
um subconjunto com soma k é justamente o que retornaria verdadeiro para o problema
original Subset Sum. Só precisamos agora explicitar que essa solução vale para o conjunto
original, pois até agora mostramos para S’, que tem um elemento a mais que S.

Isso é simples, veja que adicionamos somente um elemento, isso quer dizer que um dos
subconjuntos obtidos por Partition obrigatoriamente deve conter somente elementos do
conjunto original S, e sendo assim, é um subconjunto de S, por definição.

Sumarizando, a resposta SIM/NÃO dada por Subset Sum para S é a resposta SIM/NÃO dada
por Partition para S’. Logo, a redução está completa.

4.

NP-Difícil

NP-Completo

P: Problemas resolvíveis em tempo polinomial. Exemplo: menor caminho entre dois vértices
de um grafo com pesos positivo (dijkstra).

NP: São problemas de decisão resolvíveis em tempo polinomial se usado um algoritmo não-
determinístico, são também verificáveis em tempo polinomial. Exemplo: Circuit-SAT.

NP-Completo: Representa o conjunto de todos os problemas X em NP para os quais é possível


reduzir qualquer outro problema NP Y para X em tempo polinomial. Exemplo: 3-SAT.

Exp: Problemas resolvíveis em tempo exponencial. Exemplo: avaliar uma partida de Go.

NP-Difícil: São os problemas que são pelo menos tão difíceis quanto qualquer outro problema
em NP-Completo. Precisamente, um problema X é NP-Difícil, se existe um problema NP-
Completo Y, tal que Y é reduzível para X em tempo polinomial. Exemplo: o Problema da
Parada.

R: Conjunto dos problemas de decisão resolvíveis em tempo finito por uma máquina de
Turing, que é o conjunto de todas as linguagens recursivas (daí R).

Você também pode gostar