[AJUDA] Ansiedade por NÃO conseguir resolver Algoritmos SIMPLES
contexto: Estou estudando para a OBI
e tenho resolvido algoritmos
de todas as edições. No entanto, estou enfrentando o seguinte problema: consigo conceber a solução logicamente, mas ao tentar implementá-la, travo completamente. Já se passaram 4 horas
, estou tentando resolver esse algoritmo, que é bastante simples, e não consigo. Isso tem causado uma ansiedade intensa, a ponto de não sentir fome mais.
Escada perfeita - OBI2006
Uma construtora, durante a criação de um parque temático, encontrou no terreno um conjunto de vários pilhas de cubos de pedra. Ao invés de pagar pela remoção dos cubos de pedras, um dos arquitetos da empresa achou interessante utilizar as pedras para decoração do parque, determinando que as pedras fossem re-arranjadas no formato de “escada“ . Para isso, os funcionários deveriam mover alguns cubos para formar os degraus das escadas. Só que o arquiteto decidiu que, entre uma pilha c outra de pedras deveria haver exatamente uma pedra de diferença, formando o que ele chamou de escada perfeita. O exemplo abaixo mostra um conjunto de cinco pilhas de pedras encontradas e as cinco pilhas como ficaram após a arrumação em escada perfeita.
Dada uma sequência de pilhas de cubos de pedras com suas respectivas alturas, você deve determinar o número mínimo de pedras que precisam ser movidas para formar uma escada perfeita com exatamente o mesmo número de pilhas de pedras encontrado inicialmente (ou seja, não devem ser criadas ou eliminadas pilhas de pedras). O degrau mais baixo da escada deve sempre estar do lado esquerdo.
Entrada
A entrada contém um único conjunto de testes, que deve ser lido do dispositivo de entrada padrão (normalmente o teclado). A primeira linha contém um inteiro N que indica o número de pilhas de pedras. A segunda linha contém N números inteiros que indicam a quantidade de cubos de pedras em cada uma das pilhas, da esquerda para a direita.
Saída
Seu programa deve imprimir uma única linha, contendo um único inteiro: o número mínimo de cubos de pedras que devem ser movidos para transformar o conjunto de pilhas em uma escada perfeita, conforme calculado pelo seu programa. Caso não seja possível efetuar a transformação em escada perfeita, imprima como resultado o valor -1.
Exemplos
Entrada55 4 5 4 2 | Saída5 |
Entrada69 8 7 6 5 4 | Saída9 |
Entrada21 5 | Saída-1 |
Minha Solução
Sabemos que a escada perfeita
aumenta de 1
em 1
. Isso significa que ela é uma PA
de razão 1
.
Um pouco sobre Progressão Aritmética - PA
Uma prograssão aritmética
é uma sequência númerica
em que cada termo, partir do segundo
é a soma do termo anterior
com uma constante R
-
Exemplo de PA
{2, 3, 4, 5, 6} O primeiro termo é o
2
e arazão
é1
. -
Fórmula
`an = a1 + (n - 1).r` {2, 3, 4, 5, 6} = 2 + (3...n - 1) . 1
-
Soma de PA
Para encontrarmos a
soma
de determinadointervalo
em uma PA podemos utilizar afórmula
`Sn = n/2 . (a1 + an)`
n
= Tamanho intervalo.a1
= Posição do inicial na PA.an
= Posição do intervalo na PA.
Exemplo de soma:
Dada a seguinte
PA
= {2, 4, 6, 8, 10}, derazão
igual a2
. Determine a soma dos3
primeiros valores dessaPA
.Valores
= {2, 4, 6}n
= 3a1
= 2a3
= 6s
= 3/2 . (2 + 6) -> 12
Lógica da Solução
-
Sabemos quanto vale o
n
(o numero de pilhas) a partir dele conseguimos calcular a soma dessaPA
, basta realizar umloop
. -
Sabemos que a razão é
1
. Pois a escada deve aumentar de1
em1
degrau. -
Utilizaremos as seguintes
equações
para calcularmos o valorinicial
efinal
daPA
somaPA
= Utilizar for()valorFinal
= ( ( somaPA . 2 / n ) + n - 1 ) / 2valorInicial
= valorFinal - n + 1
Travei.
Essa foi a solução que eu encontrei. Testei apenas para os valores informados e não está muito bem escrito. Porém, espero que possa ajudar a entender como encontrar a sua solução.
Obs.: eu acho que o problema da sua solução é que você não sabe quantos blocos vai na primeira coluna, caso soubesse era, de fato, ir somando 1 à cada coluna. Além disso, se encontrar algum erro na minha solução seria ótimo saber.
def minBlocks(columns):
min_blocks = 0
for i in range(columns):
min_blocks += i + 1
return min_blocks
def buildStair(columns: int, block_list: list):
min_blocks = minBlocks(columns)
block_count = sum(block_list)
blocksLeft = block_count - min_blocks
if block_count < min_blocks or not (blocksLeft % columns == 0):
print("Impossível montar escada")
return
stair = []
for i in range(columns):
if (i == 0):
stair.append(1)
else:
stair.append(stair[i-1] + 1)
leftForEachColumn = blocksLeft // columns
stair = list(map(lambda x: x + leftForEachColumn, stair))
return stair
def movedBlocks(block_list: list, stair: list):
movedBlocks = 0
columns = len(stair)
for column in range(columns):
movedBlocks += abs(block_list[column] - stair[column])
return movedBlocks // 2
blockList = [5, 4, 5, 4, 2]
stair = buildStair(5, blockList)
print(stair)
if stair:
print(movedBlocks(blockList, stair))
blockList = [9, 8, 7, 6, 5, 4]
stair = buildStair(6, blockList)
print(stair)
if stair:
print(movedBlocks(blockList, stair))
blockList = [1, 5]
stair = buildStair(2, blockList)
print(stair)
if stair:
print(movedBlocks(blockList, stair))
Resolvi experimentar. Estou praticando Java funcional, então não estou utilizando os loops tradicionais, mas sim os range
no lugar deles.
class EscadaPerfeita {
static int calcular(final List<Integer> escada) {
var escadaPerfeita = escadaPerfeita(escada);
if (escadaPerfeita.isEmpty()) {
System.out.println(escada + " -> -1");
return -1;
}
var numeroDeMovimentos = range(0, escada.size())
.map(i -> escada.get(i) - escadaPerfeita.get(i))
.filter(i -> i > 0)
.sum();
System.out.println(escada + " -> " + escadaPerfeita + " -> " + numeroDeMovimentos);
return numeroDeMovimentos;
}
private static List<Integer> escadaPerfeita(List<Integer> escada) {
Function<Integer, List<Integer>> gerarProjecaoLinear =
(inicio) -> range(inicio, inicio + escada.size())
.boxed()
.toList();
int somaDasAlturas = escada.stream().mapToInt(Integer::intValue).sum();
return rangeClosed(0, somaDasAlturas)
.boxed()
.map(gerarProjecaoLinear)
.filter(projecaoLinear -> projecaoLinear
.stream()
.mapToInt(Integer::intValue)
.sum() == somaDasAlturas)
.findFirst()
.orElse(List.of());
}
}
Testes de unidade
class EscadaPerfeitaTest {
@ParameterizedTest
@MethodSource("dadosDeTeste")
void numeroDeMovimentosParaEscadaPerfeita(List<Integer> escada, int expectativa) {
assertEquals(expectativa, EscadaPerfeita.calcular(escada));
}
static Stream<Arguments> dadosDeTeste() {
return Stream.of(
// Dados do artigo
arguments(List.of(5, 4, 5, 4, 2), 5),
arguments(List.of(9, 8, 7, 6, 5, 4), 9),
arguments(List.of(1, 5), -1),
// Meus dados adicionais
arguments(List.of(0), 0),
arguments(List.of(7), 0),
arguments(List.of(1, 2, 3), 0),
arguments(List.of(4, 1, 1), 3),
arguments(List.of(7, 1, 1), 5),
arguments(List.of(10, 9, 8), 2),
arguments(List.of(14, 13, 7, 16, 10), 9),
arguments(List.of(15, 1, 2), 10),
arguments(List.of(5, 5), -1),
arguments(List.of(5, 6, 5), -1)
);
}
}
A saida do console (ajuda a visualizar):
[5, 4, 5, 4, 2] -> [2, 3, 4, 5, 6] -> 5
[9, 8, 7, 6, 5, 4] -> [4, 5, 6, 7, 8, 9] -> 9
[1, 5] -> -1
[0] -> [0] -> 0
[7] -> [7] -> 0
[1, 2, 3] -> [1, 2, 3] -> 0
[4, 1, 1] -> [1, 2, 3] -> 3
[7, 1, 1] -> [2, 3, 4] -> 5
[10, 9, 8] -> [8, 9, 10] -> 2
[14, 13, 7, 16, 10] -> [10, 11, 12, 13, 14] -> 9
[15, 1, 2] -> [5, 6, 7] -> 10
[5, 5] -> -1
[5, 6, 5] -> -1
Eu decidi enviar minha resposta anterior na página da contudo, mas não ultrapassou o limite de tempo de execução. Optei por reescrever utilizando os valores primitivos para tornar o processo mais rápido. Agora, todos os testes foram bem-sucedidos.
public class escada {
public static int[] lerEntrada() {
Scanner scanner = new Scanner(System.in);
int tamanho = Integer.parseInt(scanner.nextLine()); // Nao preciso da primeira linha contendo o tamanho dos numeros
String linha = scanner.nextLine();
String[] inputArray = linha.split("\\s+");
try {
int[] resultado = new int[tamanho];
for (int i = 0; i < tamanho; i++) {
resultado[i] = Integer.parseInt(inputArray[i]);
}
return resultado;
} catch (NumberFormatException e) {
System.err.println("Entrada invalida " + e.getMessage());
System.exit(-1);
}
return new int[0];
}
public static void main(String[] args) {
int[] escada = lerEntrada();
System.out.println(EscadaPerfeitaFast.calcular(escada));
}
static class EscadaPerfeitaFast {
static int calcular(final int[] sequencia) {
var progressaoAritimetica = progressaoAritimetica(sequencia);
if (progressaoAritimetica == null) {
return -1;
}
return calcularNumeroDeMovimentos(sequencia, progressaoAritimetica);
}
private static int calcularNumeroDeMovimentos(int[] escada, int[] escadaPerfeita) {
int soma = 0;
for (int i = 0; i < escada.length; i++) {
int movimentos = escada[i] - escadaPerfeita[i];
if (movimentos > 0) {
soma += movimentos;
}
}
return soma;
}
private static int[] progressaoAritimetica(int[] sequencia) {
long somaSequencia = somarSequencia(sequencia);
int primeiroTermoProgressaoAritimetica = calcularPrimeiroTermoProgressaoAritimetica(sequencia);
if (somarProgressaoAritimetica(primeiroTermoProgressaoAritimetica, sequencia.length) == somaSequencia) {
return gerarProgressaoLinear(primeiroTermoProgressaoAritimetica, sequencia.length);
}
return null;
}
private static int calcularPrimeiroTermoProgressaoAritimetica(int[] escada) {
int s = (int) somarSequencia(escada);
int n = escada.length;
return (2 * s - (n * n) + n) / (2 * n);
}
private static int[] gerarProgressaoLinear(int inicio, int tamanho) {
var resultado = new int[tamanho];
for (int i = 0; i < tamanho; i++) {
resultado[i] = inicio + i;
}
return resultado;
}
private static long somarProgressaoAritimetica(int primeiroTermo, int tamanho) {
double n = tamanho;
double a = primeiroTermo;
double sum = (n/2) * ((2 * a ) + (n - 1));
return (long) sum;
}
private static long somarSequencia(int[] sequencia) {
long soma = 0;
for (int i = 0; i < sequencia.length; i++) {
soma += sequencia[i];
}
return soma;
}
}
}