1ª Maratona de Programação do Sul - Editorial
Contents
Apoio:
A 1ª Maratona de Programação do Sul ocorreu em 16 de outubro de 2021, das 14:00 às 19:00, URI Online Judge, contando com o apoio de: Mendelics, Magrathea Labs, Driva, SBC, e URI Online Judge. Nosso sincero agradecimento a todos os apoiadores e a todos os que trabalharam para que o sonho da 1ª Maratona de Programação do Sul se tornasse realidade!
A prova consistiu em 13 problemas:
- A: Flutuação do PIB (autor: Leandro Zatesko)
- B: Ancestralidade (autor: Alesom Zorzi)
- C: Jogo das Aranhas (autor: Leandro Zatesko)
- D: Armadura (autor: Emílio Wuerges)
- E: A Casa das Sete Mulheres (autor: Leandro Zatesko)
- F: Clickbait (autor: Cristhian Bonilha)
- G: Fuja comigo, Galileu! (autor: Gabriel Galli)
- H: Araucárias e a Gralha-Azul de Três Olhos (autor: Leandro Zatesko)
- I: Joãozinho Vem Para o Sul (autor: Maicon Zatelli)
- J: Pinhões no Xadrez (autor: Leandro Zatesko)
- K: Reprodução Controlada (autor: Ricardo Pfitscher)
- L: Rico do Mate (autor: Ricardo Pfitscher)
- M: Sobrenome Não é Fácil (autor: Ricardo Pfitscher)
Nosso sincero agradecimento aos autores!
Os problemas foram realizados pelos competidores na seguinte ordem (do problema mais feito para o problema menos feito):
M A L K B C J F D I G H E
A seguir, comentários sobre cada problemas, seguindo a ordem acima.
Edição: Leandro Zatesko
Please note that being well-versed in competitive programming is not the end goal, but only a means to an end. The true end goal is to produce all-rounder computer scientists/programmers who are much readier to produce better software and to face harder CS research problems in the future.
— Steve & Felix Halim, Competitive Programming Book
M: Sobrenome Não é Fácil
Comentário do autor do problema: Ricardo Pfitscher
Este problema é bem simples. Basta procurar por cada sobrenome e contar a quantidade de não-vogais consecutivas. Ao chegar em número maior ou igual a 3 aquele sobrenome é díficil. O ponto de atenção é a primeira letra que é maiúscula (atenção ao caso) e que algumas vogais podem ter acento, assim, são consideradas consoantes.
A: Flutuação do PIB
Comentário do autor do problema: Leandro Zatesko
Seja \(f_i\) o fator de flutuação ref. ao \(i\)-ésimo ano, i.e.
\begin{equation*} f_i = 1 + F_i / 100\,. \end{equation*}Assim, o fator de flutuação ref. ao período de dois anos é dado por \(f_1 \times f_2\) e, portanto, a resposta é ser impressa é dada por
\begin{equation*} (f_1 \times f_2 - 1) \times 100 . \end{equation*}L: Rico do Mate
Comentário do autor do problema: Ricardo Pfitscher
Este problema consiste em utilizar o conceito de lista circular, diminuindo a capacidade da garrafa térmica a cada passo de iteração.
Atenção aos competidores que tendem a utilizar o resto da divisão, o que é válido. Nesse caso, é preciso ajustar as casas decimais e depois calcular a quantidade restante na térmica.
K: Reprodução Controlada
Comentário do autor do problema: Ricardo Pfitscher
A solução deste problema passa pelo mapeamento das relações de parentesco em um grafo direcionado.
Depois, o programador pode realizar uma ordenação topológica com busca em profundidade e verificar se os dois animais da linha de teste estão na mesma ordem. Outra opção é executar uma busca em profundidade a partir de cada animal do grafo gerando uma lista de animais explorados, caso os dois animais do teste estiverem no resultado da exploração, então, eles são parentes.
B: Ancestralidade
Comentário do autor do problema: Alesom Zorzi
Basicamente a ideia do problema é percorrer todos os genomas e descobrir a maior sequência que está presente em ao menos uma poupulação, de forma gulosa mesmo.
Na minha solução eu utilizei bitmask para salvar os conjuntos e não precisar percorrer todas as populações.
C: Jogo das Aranhas
Comentário do autor do problema: Leandro Zatesko
Solução 1
A primeira coisa a se observar é que não é necessário imprimir \(K\) o menor possível, basta imprimir algum \(K < 10^{16}\) que funcione. Então, vamos encontrar um \(K\) que não só faça todas as aranhas-marrons serem mortas, mas que também faça com que elas sejam mortas na ordem
\begin{equation*} 2N, 2N - 1, \dotsb, N + 1\,. \end{equation*}Para que a primeira aranha a morrer seja a aranha na posição \(2N\), é necessário que \(K\) seja um múltiplo de \(2N\). Para que a primeira aranha a morrer seja a aranha na posição \(2N\) e a segunda a morrer seja a aranha na posição \(2N - 1\), é necessário que K seja múltiplo tanto de \(2N\) quanto que de \(2N - 1\). Indutivamente, concluímos que basta tomar \(K\) como o menor múltiplo comum dos inteiros \(2N, 2N - 1, \dotsb, N + 1\). Podemos facilmente checar que, para \(N = 19\) (o maior valor possível para a entrada), esse valor de \(K\) é menor que \(10^{16}\).
O menor múltiplo comum \(lcm(A, B)\) de dois números \(A\) e \(B\) pode ser calculado dividindo-se o produto \(AB\) pelo máximo divisor comum entre \(A\) e \(B\), o qual pode ser calculado com o Algoritmo de Euclides. O menor múltiplo comum de três inteiros \(A\), \(B\), \(C\) pode ser calculado por
\begin{equation*} lcm(A, B, C) = lcm(A, lcm(B, C))\,. \end{equation*}Solução 2
Para um dado \(K\) fixo, é possível, através de operações simples de aritmética modular, simular em tempo \(O(N)\) o processo do jogo, para descobrir as \(N\) posições das aranhas mortas. Se Merlin perde o jogo, seja \(I\) tal que a $I$-ésima aranha a ser morta é uma aranha-vermelha-comum e todas as aranhas mortas antes sejam aranhas-marrons. Seja \(D\) a distância no círculo, considerando o sentido de eliminação das aranhas, entre essa aranha-vermelha-comum e a primeira aranha-marrom ainda viva. É claro que Merlin perde o jogo não apenas para \(K\), mas também para qualquer inteiro no intervalo \([K .. K + \lceil D / I\rceil - 1]\).
Assim, podemos começar com \(K = 1\), e, enquanto a simulação não termina em vitória para Merlin, incrementar \(K\) de \(\lceil D / I\rceil\). Desta forma garantimos que o valor de \(K\) encontrado é o menor possível. Não temos ideia se essa estratégia é suficiente para responder em tempo hábil o valor de \(K\) para qualquer \(N \leq 19\), mas, como sabemos que essa estratégia sempre conduz ao valor ótimo de \(K\), podemos apostar que esse valor não vai ser tão grande assim. De fato, testando localmente, percebemos que o valor de \(K\) nunca excede \(2^{31}\). No entanto, a implementação dessa estratégia não é suficiente para passar no limite de tempo, mas com ela podemos pré-processar as respostas para todas as 19 entradas possíveis e assim submeter uma solução que simplesmente consulta a resposta para a entrada correspondente.
J: Pinhões no Xadrez
Comentário do autor do problema: Leandro Zatesko
Vamos primeiro considerar a seguinte solução, ingênua, para o problema. Para cada uma das \(\leq N^2\) posições livres do tabuleiro, podemos simular o movimento de uma dama partindo daquela posição em cada uma das 8 direções possíveis, contando quantas direções estão livres, i.e. em quantas direções conseguimos mover até a borda do tabuleiro sem encontrar pinhão algum. Deste modo, conseguimos descobrir quantos pinhões uma dama colocada em cada casa livre do tabuleiro atacaria. Então, basta tomar o máximo dos valores calculados sobre todas as casas, e temos a resposta procurada. No entanto, a simulação do movimento da dama em cada uma das 8 direções possíveis custa tempo \(O(N)\), e, portanto, esta solução tem complexidade \(O(N^3)\). Como \(N \sim 10^3\), esta solução não passa no limite de tempo..
Para cada linha, cada coluna, e cada diagonal do tabuleiro, criemos um vetor das posições daquela linha/coluna/diagonal em que há pinhões. É possível criar esses vetores todos numa única varredura linear sobre o tabuleiro, i.e. em tempo \(O(N^2)\). Depois, vamos considerar cada uma das posições livres do tabuleiro, numa segunda varredura linear. Para cada posição considerada, onde queremos simular o posicionamento de uma dama, basta ver quais vetores de linha, de coluna, de diagonal ascendente, e de diagonal descendente intersectam aquela posição. Assim, tudo o que precisamos fazer, para cada um desses 4 vetores considerados, são os seguintes testes: se o vetor é vazio; se é não-vazio e a posição onde estamos simulando posicionar a dama é menor\(^{\ast}\) que a primeira posição gravada no vetor; se é não-vazio e a posição onde estamos simulando posicionar a dama é maior\(^{\ast}\) que a última posição gravada no vetor. Todos esses testes podem ser feitos em \(O(1)\), o que faz com que o tempo total da solução seja linear no tamanho da entrada, i.e. \(O(N^2)\).
\(^{\ast}\) menor/maior de acordo com a ordem das posições naquela linha/coluna/diagonal sob a qual foi construído o vetor correspondente.
Veja que não é necessário, na verdade, armazenar todo o conteúdo de cada vetor, sendo relevantes apenas o primeiro e o último elementos. No entanto, armazenar o vetor todo ou só o primeiro e o último elementos não faz diferença na eficiência da solução, assintoticamente. Fica a cargo daquilo que o programador acha mais fácil de codificar.
F: Clickbait
Comentário do autor do problema: Cristhian Bonilha
Vamos definir a função \(f(T)\) como a quantidade de views que o vídeo recebeu até o momento \(T\). É possível notar que essa função é não-decrescente, ou seja, se \(A < B\), então \(f(A) \leq f(B)\).
Isso é o suficiente para que possamos usar a técnica de busca binária para encontrarmos o menor momento em que a quantidade de views seja maior ou igual a \(K\).
Para calcular \(f(T)\), basta iterar sobre todas as pessoas e calcular quantas vezes ela já assistiu o vídeo no momento \(T\), que é igual a:
\begin{equation*} \left\{ \begin{aligned} & 0\,,&&\text{se \(T < a_i\);}\\ & 1 + \Bigl\lfloor\frac{T - a_i}{b_i}\Bigr\rfloor\,,&&\text{se \(T \geq a_i\).} \end{aligned}\right. \end{equation*}A complexidade desta solução é \(O(N \ast \lg Q)\), onde \(Q\) representa o intervalo de busca. O intervalo \([1, 2\ast 10^{18}]\) é suficiente. Como \(\lg Q\) é uma constante, pode-se dizer que a complexidade é \(O(N)\).
Note que, dependendo da entrada e do valor \(T\), o resultado de \(f(T)\) pode ultrapassar os limites de uma variável long (\(2^{64}\)). Logo, ao calcular \(f(T)\), se o valor ultrapassar \(K\), evite continuar com o cálculo antes que dê overflow.
D: Armadura
Comentário:
TBA
I: Joãozinho Vem Para o Sul
Comentário do autor do problema: Maicon Zatelli
A ideia para resolver o problema é aplicar o princípio da inclusão–exclusão de teoria dos conjuntos para remover as sobreposições de divisores e "percorrer" rapidamente o intervalo de até 1 bilhão por meio de soma de termos de uma progressão aritmética. Não devem ser aceitas soluções que tentem percorrer o intervalo de \(I\) até \(F\) de maneira trivial.
G: Fuja comigo, Galileu!
Comentário do autor do problema: Gabriel Galli
Resumidamente, a solução do problema é calcular o fecho convexo formado pelos planetas e verificar se o asteroide está dentro, exatamente em uma das arestas, ou em um dos pontos do polígono formado pelo fecho convexo. É importante perceber que o Sol é um vértice válido do fecho convexo, apesar de não ser dado na entrada. Mas pela descrição do problema e pelos cálculos que se precisa fazer (que são relativos ao Sol, mas não são dadas as coordenadas dele), deduz-se que ele se encontra em \((0, 0)\). Somando a isso o fato de o número mínimo de planetas ser 2, basta imaginar um triângulo entre os planetas e o Sol que é facilmente perceptível a validade dessa configuração perante o que é pedido.
Não precisamos guardar todos os planetas, mas sim os pontos que eles representam no plano cartesiano. Assim, ao ler cada planeta da entrada podemos já calcular as coordenadas \((x, y)\), convertidas das coordenadas polares, e guardar o respectivo ponto encontrado. Para isso, foi utilizado busca binária no intervalo de zero ao dobro da anomalia média para encontrar a anomalia excêntrica. O resto é calculado a partir das fórmulas apresentadas na descrição do problema e de fórmulas conhecidas de elipse.
Note que, por causa da precisão do double, as funções \(sin()\) e \(cos()\) não retornam exatamente 0.0 quando o retorno deveria ser 0.0. Então precisamos checar se elas retornaram 0.0 dentro do erro \(EPS\) estabelecido (\(10^{-9}\)) e aí atribuir 0.0 às respectivas variáveis. Dessa meneira, fazemos com que elas percam esses erros de precisão que causariam WA no futuro devido às multiplicações que aumentam esse erro (apenas no caso do sin(), aqui, entretanto). Na busca binária para resolver a Equação de Kepler, isso não é necessário porque a multiplicação é com algo entre 0 e 1. Todas as operações de comparação com as variáveis double estão sendo feitas com macros que consideram esse erro \(EPS\).
O algoritmo para encontrar o fecho convexo é o mesmo encontrado no livro dos Halim, com algumas alterações para atender as condições do problema:
- o uso do \(EPS\), para tratar aqui também os erros de precisão do double;
- sempre retornar um fecho vazio caso tenhamos menos que três pontos no vetor passado como referência, ou ele tenha três pontos e eles sejam colineares, ou todos os pontos são colineares. No caso de três pontos não colineares, os Halim readicionam o ponto 0 ao fim do vetor e retornam ele mesmo. Entretanto, precisamos garantir que os pontos estão ordenados para o código que verifica se um ponto está dentro de um polígono funcione, então deixo esse caso ser executado normalmente.
Para verificar se um ponto está dentro de um polígono, os Halim utilizam a função que funciona para qualquer polígono somando os ângulos e verificando se encontramos \(2 \ast PI\). Entretanto, o método para polígonos convexos que apenas verifica se o ponto está sempre à esquerda do vetor definido por dois pontos consecutivos do fecho convexo é suficiente para este problema.
Foram utilizados os seguintes sites como referência e apoio no desenvolvimento deste problema:
- https://en.wikipedia.org/wiki/Kepler's_laws_of_planetary_motion#Position_as_a_function_of_time
- https://en.wikipedia.org/wiki/Eccentricity_(mathematics)#Ellipses
- http://mathworld.wolfram.com/KeplersEquation.html (veja que há um gráfico mostrando a relação entre M e E: uma curva nunca decrescente).
H: Araucárias e a Gralha-Azul de Três Olhos
Comentário do autor do problema: Leandro Zatesko
Suponhamos que os valores de \(H_i\) para todo \(i\) já estejam computados (discutiremos sobre isso na sequência). A primeira coisa a se observar sobre este problema é a evidente propriedade gulosa que tem sua estrutura combinatória. Seja \(N(i)\) o conjunto de todas as Anciãs para as quais a Anciã \(i\) pode enviar uma mensagem diretamente (num único voo). Seja também \(nxt[i]\) (\(pre[i]\)) o menor (maior) índice \(j > i\) (\(j < i\)) tal que \(H_j > H_i\), i.e. o "Next (Previous) Greater Element" de \(i\), definindo \(nxt[i] = n + 1\) (\(pre[i] = 0\)) caso não haja tal \(j\). Os arrays \(nxt[]\) e \(pre[]\) podem ser computados através de uma varredura linear cada um, com o auxílio de uma pilha. Como \(N(i)\) coincide com o intervalo inteiro fechado entre \(nxt[i]\) e \(pre[i]\), podemos observar que \(N(nxt[i])\) inclui \(N(i) \setminus \{pre[i]\}\) e que \(N(pre[i])\) inclui \(N(i) \setminus \{nxt[i]\}\). Desta propriedade de inclusão de vizinhanças segue a propriedade gulosa: se a Anciã \(i\) quer enviar uma mensagem para a Anciã \(j > i\) (\(j < i\)) e \(j \notin N(i)\), então sempre compensa que no primeiro voo a Gralha-Azul leve a mensagem para a Anciã \(k\) que "enxergue o mais longe possível", i.e. para a qual o maior (menor) valor em \(N(k)\) seja o maior (menor) possível.
Consideremos, então, uma query "\(i\quad j\)", supondo que \(j > i\) (o caso \(j < i\) é simétrico). Alguém poderia erroneamente concluir que o problema se resume a encontrar o menor valor de \(\ell\) tal que \(nxt^l[i] \geq j\), sendo \(nxt^l[i]\) definido por: \(nxt^1[i] = nxt[i]\); \(nxt^{\ell}[i] = nxt[nxt^{\ell - 1}[i]]\) para \(\ell > 1\). No entanto, o exemplo discutido no enunciado do problema já evidencia que essa estratégia nem sempre é ótima, pois às vezes pode compensar fazer com a Gralha-Azul voe para \(pre[i]\), quando \(pre[i]\) "enxerga mais longe" que \(nxt[i]\). Redefinindo \(nxt^{\ell}[i]\) como abaixo, contudo, a estratégia funciona:
\begin{align*} nxt^1[i] &= nxt[i]\,;\\ nxt^{\ell}[i] &= \max(nxt[nxt^{\ell - 1}[i]], nxt[pre^{\ell-1}[i]]),\quad \text{para }\ell > 1\,. \end{align*}Simetricamente:
\begin{align*} pre^1[i] &= pre[i]\,;\\ pre^{\ell}[i] &= \min(pre[nxt^{\ell - 1}[i]], pre[pre^{\ell-1}[i]]),\quad \text{para }\ell > 1\,. \end{align*}Isto é, sob estas definições, o problema de fato se resume, para cada query "\(i\quad j\)" com \(j > i\) (\(j < i\)), a encontrar o menor valor de \(\ell\) tal que \(nxt^{\ell}[i] \geq j\) (\(pre^{\ell}[i] \leq j\)). Temos, no entanto, \(8\times 10^4\) queries para responder. É de se supor que, em casos extremos, o menor valor de \(\ell\) que estamos procurando seja linearmente proporcional a \(N\). Assim, não podemos nem nos dar ao luxo de encontrar esse valor de \(\ell\) em tempo \(O(N)\) por query, nem tampouco querer precomputar uma tabela de tamanho \(O(N^2)\) relacionando todas as Anciãs com todos os valores possíveis de \(\ell\). Uma estratégia possível (provavelmente não a única) para responder eficientemente às queries, contudo, é usar a técnica de "salto binário" (Binary Lifting), precomputando (iterativamente e de modo bottom–up, com o laço mais externo em \(\ell\)) as tabelas para \(nxt\) e \(pre\) de modo que cada entrada \(nxt[\ell][i]\) represente não \(nxt^{\ell}[i]\), mas \(nxt^{2^{\ell}}[i]\). Assim, cada query pode ser respondida em tempo \(O((\lg N)^2)\).
Para calcular os valores \(h_i\) (e assim obter os valores \(H_i = h_i + a_i\)), o recomendado é utilizar o Autômato de Aho–Corasick. Há de se tomar um cuidado, no entanto. Em muitas implementações do autômato encontradas na Internet, o que se faz, durante o processamento do texto, é: a cada nó visitado, navegar pelos links de saída (exit links) incrementando o número de ocorrências de todas as palavras do dicionário gravadas nesses nós. Isso faz com que a complexidade de construção do autômato mais a complexidade de processamento do texto seja linear na soma dos seguintes números: o tamanho total das palavras do dicionário; o tamanho do texto; o número total de pares \((w, i)\) sendo que \(w\) é uma palavra do dicionário que ocorre na posição \(i\) do texto. Com esta implementação provavelmente não seja possível passar no limite de tempo do problema, mas há como facilmente trocar o número total de pares \((w, i)\) como especificado pelo número total de pares \((u, i)\) sendo que \(u\) é um nó do autômato e todas as palavras do dicionário gravadas no nó \(u\) ocorrem na posição \(i\). Para tanto, basta colocar um contador dentro do nó e incrementar apenas esse contador durante o processamento do texto. Assim, ao final do processamento, é possível percorrer o autômato linearmente propagando os valores dos contadores dentro de cada nó a todas as palavras do dicionário gravadas naquele nó.
E: A Casa das Sete Mulheres
Comentário do autor do problema: Leandro Zatesko
Apesar de o enunciado deste problema, à primeira impressão, remeter a alguma variante do problema de dominação em grafos, o fato de que toda província possui exatamente uma província-irmã, podendo apenas no máximo uma delas receber um diretório, permite-nos modelar o problema como uma variante do problema da satisfazibilidade booleana (SAT). Podemos supor, sem perda de generalidade, que, para cada par de províncias-irmãs, exatamente uma delas recebe um diretório, pois, se há alguma solução válida para o problema em que ambas as províncias de um par de províncias-irmãs ficam sem receber diretório, então atribuir arbitrariamente um diretório para uma delas não altera a viabilidade da solução. Formamos, então, uma fórmula booleana na forma normal conjuntiva da seguinte maneira:
- Associamos a cada par de províncias-irmãs uma variável \(x\) das \(N\) variáveis da fórmula, sendo uma das duas províncias associada ao literal \(x\), e a outra ao literal \(\neg x\);
- Para cada província \(i\), criamos uma cláusula na fórmula que contém o literal associado a \(i\), o qual chamaremos da cabeça da cláusula, e os 6 literais correspondentes às províncias diretamente alcançáveis a partir de \(i\), compondo o que chamaremos de cauda da cláusula.
Nosso objetivo é encontrar uma valoração das variáveis da fórmula que, para cada cláusula \(C\), satisfaça a cabeça de \(C\), ou então satisfaça ao menos dois literais da cauda de \(C\). Não só é garantida a existência de tal valoração pelo enunciado do problema, como também é prometido que existe uma valoração \(T^{\ast}\) que, para cláusula \(C\), satisfaz a cabeça de \(C\) e ao menos três literais da cauda de \(C\), ou então satisfaz ao menos quatro literais da cauda de \(C\).
Ideia 1: WRONG ANSWER
Seja \(L\) o conjunto dos literais satisfeitos por \(T^{\ast}\). Como cada cláusula possui ao menos 3 ou 4 literais de \(L\), o número total de ocorrências dos literais de \(L\) em cláusulas da fórmula seguramente é maior que o número total de ocorrências dos literais que não estão em \(L\). Então, uma estratégia para resolver o problema seria, simplesmente, para cada variável \(x\), ver qual dos literais \(x\) ou \(\neg x\) ocorre mais vezes na fórmula, e então valorar \(x\) de modo a satisfazer o literal que mais ocorre. Essa ideia, contudo, não funciona, pois o fato do número total de ocorrências dos literais em \(L\) ser grande não implica que o número de ocorrências de todo literal em \(L\) seja também grande individualmente, nem garante que haja alguma valoração em que todos os literais satisfeitos sejam justamente os que mais ocorrem.
Ideia 2: TIME LIMIT EXCEEDED
Da garantia da existência da valoração \(T^{\ast}\), e do fato de a exigência a que nossa valoração precisa atender ser muito mais frouxa do que a restrição a que \(T^{\ast}\) atende, podemos intuir, corretamente, que há provavelmente muitas valorações possíveis que sirvam como solução. Podemos, então, sortear uma valoração, ver se ela serve, e, caso não sirva, ressortear até encontrar uma valoração que sirva. Essa abordagem, contudo, não serve. As instâncias são muito grandes, há muitas delas, e a chance de o algoritmo dar sorte para todas elas é infinitesimal.
Solução: ACCEPTED
(Inspirada no algoritmo probabilísitico para 2SAT de Papadimitriou)
Vamos começar sorteando uma valoração \(T\). Se \(T\) não serve, vamos sortear uma cláusula \(C\) em que a exigência do enunciado falha para \(T\), i.e. \(T\) não satisfaz a cabeça de \(C\) e \(T\) satisfaz no máximo um literal da cauda de \(C\). Temos, então, duas opções de operações para com certeza consertar \(T\) em relação a \(C\):
- operação 1: inverter a valoração da variável na cabeça de \(C\);
- operação 2: sortear, sob distribuição uniforme, algum dos pelo menos 5 literais da cauda de \(C\) que não são satisfeitos, e então inverter a valoração da variável do literal sorteado.
Claro que qualquer uma das operações pode desencadear efeitos colaterais em outras cláusulas da fórmula. Seja \(i\) o número de variáveis em que \(T\) e \(T^{\ast}\) concordam. Após realizar qualquer uma das operações 1 ou 2, esse número torna-se \(i + 1\) (sucesso) ou \(i - 1\) (falha). Queremos uma estratégia que garanta que a probabilidade de sucesso seja sempre pelo menos \(1/2\). Assim, da teoria de passeios aleatórios (random walks) em grafos, teremos que um número esperado de \(N^2\) iterações desse procedimento será o suficiente para encontramos a valoração que desejamos.
Não controlamos a probabilidade de \(T^{\ast}\) satisfazer ou não a cabeça da cláusula sorteada \(C\). Assim, para garantir que a probabilidade de sucesso a cada iteração seja pelo menos \(1/2\), é suficiente garantir que a probabilidade é pelo menos \(1/2\) tanto condicionada a que \(T^{\ast}\) satisfaz a cabeça de \(C\), quanto condicionada a que \(T^{\ast}\) não satisfaz a cabeça de \(C\). Precisamos, então, definir uma probabilidade \(p\) para escolhermos realizar a operação 1, o que implica que realizaremos a operação 2 com probabilidade \(1 - p\).
Se \(T^{\ast}\) satisfaz a cabeça de \(C\), há pelo menos 3 literais da cauda de \(C\) que \(T^{\ast}\) também satisfaz. Assim, alcançamos sucesso se:
- realizamos a operação 1, o que fazemos com probabilidade \(p\);
ou:
- realizamos a operação 2 e conseguimos inverter algum literal da cauda em que \(T\) e \(T^{\ast}\) não concordavam, o que fazemos com probabilidade pelo menos: \((1 - p)(3/6)\), se nenhum literal da cauda era satisfeito por \(T\); \((1 - p)(2/5)\), se \(T\) satisfazia um literal da cauda de \(C\).
Assim, a probabilidade de sucesso é pelo menos \(p + (1 - p)(2/5)\).
Por outro lado, se \(T^{\ast}\) não satisfaz a cabeça de \(C\), há pelo menos 4 literais da cauda de \(C\) que \(T^{\ast}\) também satisfaz. Assim, temos sucesso se:
- realizamos a operação 2 e conseguimos inverter algum literal da cauda de em que \(T\) e \(T^{\ast}\) não concordavam, o que fazemos com probabilidade pelo menos: \((1 - p)(4/6)\), se nenhum literal da cauda era satisfeito por \(T\); \((1 - p)(3/5)\), se \(T\) satisfazia um literal da cauda de \(C\).
Assim, a probabilidade de sucesso é pelo menos \((1 - p)(3/5)\).
Resolvendo
\begin{equation*} \left\{ \begin{aligned} p + (1 - p)(2/5)&\geq 1/2\quad\text e\\ (1 - p)(3/5) &\geq 1/2\,, \end{aligned} \right. \end{equation*}temos \(p = 1/6\). Assim, sob essa estratégia, o número esperado de iterações do algoritmo é \(N^2\). Pode parecer muito, uma vez que \(N \sim 10^4\), mas temos que lembrar que as contas foram feitas em relação a um referencial fixo \(T^{\ast}\), quando, na verdade, as exigências a que estamos sujeitos são mais frouxas, e é de se intuir que muitas outras valorações sirvam.
Só precisamos agora garantir que cada iteração do algoritmo possa ser executada eficientemente. Podemos fazer isso mantendo o \(score[]\) de cada cláusula, que é o número de literais satisfeitos na cauda da cláusula \(+\) 2 se a cabeça também é satisfeita. Assim, mantendo tanto o grafo direto quanto o grafo reverso da instância, e mantendo um array \(a[]\) das cláusulas que ainda têm \(score[] \leq 1\), cada operação de inversão de valoração de uma variável pode ser executada em tempo amortizado constante (cada literal participa de um número amortizado constante de cláusulas). Para implementar também em tempo constante a remoção de uma cláusula do array \(a[]\), basta trocarmos de posição a cláusula a ser removida com a cláusula no final do array, e então encolhermos o array em uma posição.