Reforço de Algoritmos e Programação - Instituto de Informática (UFRGS) - Prof. Dennis Giovani Balreira



Aula 7 - Revisão da Área I

Esta aula não traz conteúdo novo: é uma revisão rápida de tudo que vimos até aqui (Aulas 1 a 6), seguida de exercícios integradores — problemas maiores, inspirados em questões de provas anteriores, que combinam vários dos conceitos da Área I ao mesmo tempo. Nenhum desses exercícios exige o uso de funções (assunto da Área II) — tudo pode, e deve, ser resolvido dentro de uma única main().


Revisão rápida, aula por aula

Aula 1 — Introdução, variáveis, memória, expressões e atribuição.
  • Algoritmo (passos) × programa (implementação em uma linguagem); compilação (gcc) × execução.
  • Variáveis: int, float, double, char; cada uma ocupa um número fixo de bytes na memória.
  • printf/scanf (com & para variáveis simples); operadores aritméticos (+ - * / %), atenção à divisão inteira; atribuição (=) e operadores compostos (+=, ++).
Aula 2 — Expressões relacionais/lógicas e comandos condicionais.
  • Relacionais (> < >= <= == !=) e lógicos (&& || !, com curto-circuito); cuidado com = × ==.
  • if, if/else, cadeias de else if (a ordem dos testes importa); indentação consistente.
  • switch/case/break para várias alternativas de uma mesma variável; heurística: pense primeiro no switch, mas o if/else é o mais genérico dos dois.
Aula 3 — Comandos de repetição e suas aplicações.
  • while (teste desconhecido de antemão), do-while (executa ao menos uma vez), for (contador conhecido) — o for nada mais é do que um while com inicialização/condição/atualização reunidas em uma linha.
  • Contadores (contam ocorrências), acumuladores (somam/multiplicam), flags (lembram um evento) — quase todo laço usa um desses padrões.
  • Laços aninhados (um for dentro de outro), validação de entrada com while, menus com do-while.
Aula 4 — Vetores.
  • Vetor: várias posições do mesmo tipo, sob um único nome; índices de 0 a tamanho - 1; C não verifica limites automaticamente.
  • Elementos ficam contíguos na memória; for é a estrutura natural para percorrer um vetor.
  • Padrões comuns: soma/média (acumulador), maior/menor elemento, busca sequencial.
Aula 5 — Strings e matrizes.
  • String: vetor de char terminado por '\0'; scanf("%s",...) não deve ser usado para texto com espaços; prefira fgets a gets (inseguro); strlen, strcpy, strcmp, strcat (nunca compare strings com ==).
  • Conversão número ↔ string: atoi/atof e snprintf.
  • Matriz: vetor de duas dimensões ([linha][coluna]), percorrida com dois fors aninhados; dimensões extras existem, mas usar com moderação.
Aula 6 — Busca linear, busca binária, ordenação e números pseudoaleatórios.
  • Complexidade: intuição de custo "da ordem de n", "" e "log n", comparando o que acontece quando o vetor dobra de tamanho.
  • Busca linear: percorre o vetor um a um, com while (parando assim que encontra); pior caso é da ordem de n comparações.
  • Busca binária: só funciona em vetor ordenado; compara sempre com o meio e descarta metade do intervalo a cada passo; pior caso da ordem de log n — bem mais rápida que a busca linear.
  • Selection Sort e Bubble Sort: dois algoritmos simples de ordenação, ambos da ordem de comparações no pior caso.
  • srand(time(NULL)) uma única vez, rand() % n para limitar a faixa, somar um deslocamento para começar em outro valor (ex: dado d20 = 1 + rand() % 20).

Exercícios integradores

Os quatro problemas a seguir são inspirados em questões de provas de disciplinas de programação, adaptados para usar apenas os conceitos já vistos até aqui.

Exercício 1 — Conclave papal

No Vaticano, a eleição do Papa ocorre em um conclave, onde cardeais votam em rodadas sucessivas até que um candidato obtenha pelo menos 2/3 dos votos em uma mesma rodada. Faça um programa em C que simule esse processo:

  • Leia os nomes de NUM_CAND cardeais candidatos, armazenando-os em uma matriz de strings (um nome por linha). O identificador de cada candidato é o índice da sua linha.
  • Simule rodadas de votação: em cada rodada, leia o voto de cada um dos NUM_VOT cardeais votantes (o identificador do candidato escolhido), armazenando os votos em uma matriz onde cada linha é uma rodada e cada coluna é um votante.
  • As rodadas se encerram quando algum candidato atingir 2/3 dos votos totais naquela rodada, ou ao final da 5ª rodada sem resultado (nesse caso, considere eleito o candidato com mais votos na última rodada). Ao final de cada rodada sem vencedor, exiba "Fumaca preta!".
  • Ao decidir a eleição, exiba "Fumaca branca!", a matriz completa de votos, e o nome do Papa eleito.

Use NUM_CAND = 3 e NUM_VOT = 6 para teste.

Exercício 2 — Batalha de RPG

Faça um programa em C que simule uma batalha entre dois personagens:

  • Cada personagem começa com uma quantidade de pontos de vida, lida do usuário (podem ser diferentes para cada um).
  • A cada rodada, os dois jogadores atacam, alternadamente, jogando um dado de 6 lados: se o dado der 1, o ataque falha (sem dano); se der 6, causa 12 de dano; para valores de 2 a 5, o dano é igual ao valor sorteado.
  • O adversário perde pontos de vida iguais ao dano recebido.
  • A batalha continua até que, ao final de uma rodada completa, algum personagem tenha vida <= 0. Ao final, informe o vencedor (Jogador 1 ou Jogador 2) ou empate (se ambos chegarem a <= 0 na mesma rodada).

Exercício 3 — Cadastro de produtos de mercado

Escreva um programa que gerencie até 100 produtos de um mercado, guardados em três vetores paralelos, relacionados pelo mesmo índice (Aula 5): nome (matriz de strings), preco (vetor de float) e estoque (vetor de int). O programa deve exibir um menu (do-while + switch, Aulas 2 e 3) com as opções:

  • Cadastrar produto: lê nome, preço e estoque, e insere na próxima posição livre (se ainda houver espaço).
  • Remover último produto: remove o último produto cadastrado (se houver algum).
  • Buscar por nome: imprime o primeiro produto cadastrado com o nome informado (busca linear, Aula 6).
  • Buscar por estoque: imprime todos os produtos com estoque menor que um valor informado.
  • Atualizar preços: aumenta o preço de todos os produtos cadastrados em 20%.
  • Sair: encerra o programa com uma mensagem de despedida.

Exercício 4 — Binarização de imagem

Uma imagem em tons de cinza pode ser representada como uma matriz de pixels, cada um com um valor inteiro entre 0 (preto) e 255 (branco). A binarização transforma cada valor em 0 ou 255, de acordo com um valor limite (threshold): valores acima do limite viram 255; valores abaixo viram 0.

Escreva um programa que leia uma matriz quadrada N x N (N = 3 para teste) com valores entre 0 e 255, calcule o threshold como a média de todos os valores da matriz, e gere uma segunda matriz binarizada de acordo com essa regra, imprimindo o resultado ao final.

Exercício 5 — Menu de vendas da sorveteria

Uma sorveteria vende 3 produtos diferentes, cada um com um preço, já armazenados em 3 vetores (um deles um vetor de strings com os nomes). Ofereça ao cliente um menu com apenas 2 opções: 1 – Comprar e 2 – Sair. A cada passagem pelo menu, se a opção for comprar, apresente os produtos disponíveis (usando os dados dos vetores, nunca escritos "na mão" no meio do texto), leia o código do produto escolhido e a quantidade de unidades, e imprima o valor total a pagar (também calculado a partir dos vetores).

Exercício 6 — Quadrado perfeito pela soma de ímpares

Se somarmos os números ímpares consecutivos (1 + 3 + 5 + 7 + ...) até que a soma seja igual a um número N, a quantidade M de termos somados será igual à raiz quadrada de Nmas apenas se N for um quadrado perfeito. Por exemplo, para N = 16: 1 + 3 + 5 + 7 = 16, com M = 4 termos, e de fato 4² = 16.

Escreva um programa que leia um número inteiro positivo N (faça a consistência necessária) e informe se N é um quadrado perfeito, utilizando a soma de ímpares consecutivos descrita acima (sem usar nenhuma função de raiz quadrada pronta).

Exercício 7 — Estoque em múltiplas lojas

Uma empresa possui um programa de controle de estoque que usa dois arranjos: uma matriz ESTOQUE, onde a primeira dimensão representa os 50 produtos vendidos e a segunda dimensão representa as 4 lojas da empresa; e um vetor ESTOQUE_TOTAL, com o total em estoque de cada produto somando as 4 lojas.

Escreva um programa que: (a) preencha a matriz ESTOQUE por leitura; (b) calcule o vetor ESTOQUE_TOTAL a partir da matriz; e (c) emita um relatório com os códigos (iguais aos índices) dos produtos que têm estoque inferior a 10 unidades em qualquer uma das lojas, indicando também em qual loja isso ocorre.

Exercício 8 — Série que converge para n/(n+1)

Considere a série infinita cujo termo geral, para um valor real n informado pelo usuário, é:

termo(k) = n / [(n + k) * (n + k + 1)],  para k = 1, 2, 3, ...

A soma dos infinitos termos dessa série converge para n / (n + 1). Escreva um programa que leia um valor real n e um valor de tolerância, e calcule a soma aproximada dessa série, parando de somar termos assim que o valor absoluto do último termo calculado for menor ou igual à tolerância informada. Utilize a função fabs(valor), de <math.h>, que retorna o valor absoluto de um real.

Exercício 9 — Ocorrências de uma string dentro de outra

Escreva um programa que leia uma string s1 (com até MAXTXT caracteres) e uma string s2 (com até MAXPAL caracteres, sempre menor que s1), e informe quantas vezes s2 aparece dentro de s1 (incluindo ocorrências sobrepostas). Exemplo:

Digite string s1: Araras e maresias ao mar
Digite string s2: ar
A string ar ocorre 3 vezes

Exercício 10 — Livro mais vendido em cada loja

Uma rede com L livrarias vende os mesmos T títulos de livros em todas as lojas. Escreva um programa que leia e armazene os T títulos vendidos (até 80 caracteres cada) e, em uma matriz L x T, a quantidade vendida de cada título em cada loja no último mês. Em seguida, o programa deve repetidamente ler o código de uma loja (o índice da loja) e informar o(s) título(s) mais vendido(s) naquela loja (pode haver empate, e todos os empatados devem ser exibidos). Quando o usuário informar um código de loja inválido, o programa deve parar, exibindo "Finalizando sistema de consulta". Use L = 4 e T = 4 para teste.

Exercício 11 — Pontos mais distantes no plano cartesiano

Escreva um programa que leia as coordenadas (x, y) de N pontos no plano cartesiano (N = 5 para teste), guardando as coordenadas x em um vetor e as coordenadas y em outro. Em seguida, encontre e informe a maior distância entre dois pontos quaisquer dentre os N lidos, usando a distância euclidiana:

distancia(p1, p2) = sqrt((x1 - x2)² + (y1 - y2)²)

A função sqrt(valor), também de <math.h>, calcula a raiz quadrada de um valor real.


Sugestões de Soluções

Exercício 1 — Conclave papal
#include <stdio.h>

#define NUM_CAND 3
#define NUM_VOT 6
#define MAX_RODADAS 5

int main() {
    char nomes[NUM_CAND][50];
    int votos[MAX_RODADAS][NUM_VOT];
    int contagem[NUM_CAND];
    int c, v, r;
    int rodadasRealizadas = 0;
    int papaEleito = -1;

    // (a) leitura dos candidatos
    printf("Votacao Conclave:\n");
    for (c = 0; c < NUM_CAND; c++) {
        printf("Cardeal %d: ", c);
        scanf("%s", nomes[c]);
    }

    // (b) simulação das rodadas
    for (r = 0; r < MAX_RODADAS && papaEleito == -1; r++) {
        printf("Digite os votos da rodada %d: ", r + 1);
        for (v = 0; v < NUM_VOT; v++) {
            scanf("%d", &votos[r][v]);
        }
        rodadasRealizadas++;

        // contagem de votos desta rodada
        for (c = 0; c < NUM_CAND; c++) {
            contagem[c] = 0;
        }
        for (v = 0; v < NUM_VOT; v++) {
            contagem[votos[r][v]]++;
        }

        // verifica se algum candidato atingiu 2/3 dos votos
        for (c = 0; c < NUM_CAND; c++) {
            if (contagem[c] * 3 >= NUM_VOT * 2) {
                papaEleito = c;
            }
        }

        if (papaEleito == -1) {
            printf("Fumaca preta!\n");
        }
    }

    // se ninguem atingiu 2/3 apos 5 rodadas, vence quem teve mais votos na ultima rodada
    if (papaEleito == -1) {
        papaEleito = 0;
        for (c = 1; c < NUM_CAND; c++) {
            if (contagem[c] > contagem[papaEleito]) {
                papaEleito = c;
            }
        }
    }

    // (c) resultado final
    printf("Fumaca branca!\n");
    printf("Matriz com todos os votos:\n");
    for (r = 0; r < rodadasRealizadas; r++) {
        for (v = 0; v < NUM_VOT; v++) {
            printf("%d ", votos[r][v]);
        }
        printf("\n");
    }
    printf("Papa eleito: %s\n", nomes[papaEleito]);

    return 0;
}

Pontos-chave: contagem[votos[r][v]]++ usa o próprio voto lido como índice do vetor de contagem — um truque comum quando os valores possíveis já são índices válidos (Aula 4). A comparação contagem[c] * 3 >= NUM_VOT * 2 evita divisão (e a imprecisão de trabalhar com fração), verificando contagem[c] >= (2/3) * NUM_VOT de forma equivalente, apenas multiplicando cruzado.


Exercício 2 — Batalha de RPG
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int main() {
    int vida1, vida2;
    int dado, dano;
    int rodada = 1;

    srand(time(NULL));

    printf("Digite a vida do Jogador 1: ");
    scanf("%d", &vida1);
    printf("Digite a vida do Jogador 2: ");
    scanf("%d", &vida2);

    while (vida1 > 0 && vida2 > 0) {
        printf("Rodada %d:\n", rodada);

        // turno do jogador 1, ataca o jogador 2
        dado = 1 + rand() % 6;
        if (dado == 1) {
            dano = 0;
        } else if (dado == 6) {
            dano = 12;
        } else {
            dano = dado;
        }
        printf("Jogador 1: dado %d\n", dado);
        vida2 -= dano;

        // turno do jogador 2, ataca o jogador 1
        dado = 1 + rand() % 6;
        if (dado == 1) {
            dano = 0;
        } else if (dado == 6) {
            dano = 12;
        } else {
            dano = dado;
        }
        printf("Jogador 2: dado %d\n", dado);
        vida1 -= dano;

        printf("\n");
        rodada++;
    }

    printf("Batalha finalizada!\n");
    if (vida1 <= 0 && vida2 <= 0) {
        printf("Empate!\n");
    } else if (vida1 <= 0) {
        printf("Vencedor: Jogador 2\n");
    } else {
        printf("Vencedor: Jogador 1\n");
    }

    return 0;
}

A condição do while (vida1 > 0 && vida2 > 0) garante que a rodada inteira (os dois turnos) sempre é concluída antes de verificar o fim de jogo — exatamente como pede o enunciado ("ao final de uma rodada"), mesmo que um dos jogadores já tenha chegado a zero no meio da rodada.


Exercício 3 — Cadastro de produtos de mercado
#include <stdio.h>
#include <string.h>

#define MAX_PRODUTOS 100

int main() {
    char nome[MAX_PRODUTOS][50];
    float preco[MAX_PRODUTOS];
    int estoque[MAX_PRODUTOS];
    int total = 0;
    int opcao, i, encontrado;
    char nomeBusca[50];
    int limiteEstoque;

    do {
        printf("\n1-Cadastrar 2-Remover ultimo 3-Buscar por nome ");
        printf("4-Buscar por estoque 5-Atualizar precos 6-Sair\n");
        printf("Escolha: ");
        scanf("%d", &opcao);

        switch (opcao) {
            case 1: // cadastrar
                if (total >= MAX_PRODUTOS) {
                    printf("Capacidade maxima atingida!\n");
                } else {
                    printf("Nome: ");
                    scanf("%s", nome[total]);
                    printf("Preco: ");
                    scanf("%f", &preco[total]);
                    printf("Estoque: ");
                    scanf("%d", &estoque[total]);
                    total++;
                }
                break;

            case 2: // remover ultimo
                if (total == 0) {
                    printf("Nao ha produtos cadastrados!\n");
                } else {
                    total--;
                    printf("Produto '%s' removido.\n", nome[total]);
                }
                break;

            case 3: // buscar por nome (busca linear)
                printf("Nome a buscar: ");
                scanf("%s", nomeBusca);
                encontrado = -1;
                for (i = 0; i < total && encontrado == -1; i++) {
                    if (strcmp(nome[i], nomeBusca) == 0) {
                        encontrado = i;
                    }
                }
                if (encontrado != -1) {
                    printf("%s - R$ %.2f - %d unidades\n",
                           nome[encontrado], preco[encontrado], estoque[encontrado]);
                } else {
                    printf("Produto nao encontrado.\n");
                }
                break;

            case 4: // buscar por estoque
                printf("Estoque limite: ");
                scanf("%d", &limiteEstoque);
                for (i = 0; i < total; i++) {
                    if (estoque[i] < limiteEstoque) {
                        printf("%s - R$ %.2f - %d unidades\n",
                               nome[i], preco[i], estoque[i]);
                    }
                }
                break;

            case 5: // atualizar precos (+20%)
                for (i = 0; i < total; i++) {
                    preco[i] = preco[i] * 1.2;
                }
                printf("Precos atualizados!\n");
                break;

            case 6:
                printf("Encerrando o programa. Ate mais!\n");
                break;

            default:
                printf("Opcao invalida!\n");
        }
    } while (opcao != 6);

    return 0;
}

Os três vetores (nome, preco, estoque) são paralelos: a posição i de cada um descreve o mesmo produto, ideia já discutida na Aula 5 como alternativa a estruturas com muitas dimensões. A variável total guarda quantas posições estão realmente em uso, e é ela quem controla os limites de todos os laços — "remover" o último produto é tão simples quanto decrementar total, sem precisar apagar fisicamente nenhum dado.


Exercício 4 — Binarização de imagem
#include <stdio.h>

#define N 3

int main() {
    int m1[N][N], m2[N][N];
    int i, j;
    int soma = 0;
    float threshold;

    // leitura da matriz original
    for (i = 0; i < N; i++) {
        for (j = 0; j < N; j++) {
            printf("Digite o pixel [%d][%d]: ", i, j);
            scanf("%d", &m1[i][j]);
            soma += m1[i][j];
        }
    }

    // threshold = média de todos os valores da matriz
    threshold = soma / (float) (N * N);

    // binarização: gera m2 a partir de m1 e do threshold
    for (i = 0; i < N; i++) {
        for (j = 0; j < N; j++) {
            if (m1[i][j] > threshold) {
                m2[i][j] = 255;
            } else {
                m2[i][j] = 0;
            }
        }
    }

    // m2 já contém o resultado correto (impressão é opcional, conforme o enunciado)
    for (i = 0; i < N; i++) {
        for (j = 0; j < N; j++) {
            printf("%d ", m2[i][j]);
        }
        printf("\n");
    }

    return 0;
}

O threshold é calculado durante a própria leitura da matriz, acumulando a soma de todos os pixels em um único laço duplo, e só depois dividindo pelo número total de elementos (N * N) — o cast (float) é necessário para evitar a divisão inteira (Aula 1) e obter um threshold com casas decimais.


Exercício 5 — Menu de vendas da sorveteria
#include <stdio.h>

#define NUM_PRODUTOS 3

int main() {
    char nomes[NUM_PRODUTOS][30] = {"Casquinha", "Picole", "Sundae"};
    float precos[NUM_PRODUTOS] = {3.50, 4.00, 7.90};
    int opcao, codigo, quantidade, i;

    do {
        printf("\n1 - Comprar\n2 - Sair\nEscolha: ");
        scanf("%d", &opcao);

        if (opcao == 1) {
            printf("Produtos disponiveis:\n");
            for (i = 0; i < NUM_PRODUTOS; i++) {
                printf("%d - %s (R$ %.2f)\n", i, nomes[i], precos[i]);
            }

            printf("Codigo do produto: ");
            scanf("%d", &codigo);
            printf("Quantidade: ");
            scanf("%d", &quantidade);

            if (codigo >= 0 && codigo < NUM_PRODUTOS) {
                printf("Total a pagar: R$ %.2f\n", precos[codigo] * quantidade);
            } else {
                printf("Codigo invalido!\n");
            }
        }
    } while (opcao != 2);

    printf("Ate a proxima!\n");
    return 0;
}

Repare que o menu de produtos, na hora de exibir as opções, é impresso com um for percorrendo nomes e precos — nada é "escrito na mão" (como "1 - Casquinha" fixo no código); se os vetores mudassem, o menu se atualizaria sozinho. O mesmo vale para o cálculo do total: precos[codigo] * quantidade usa diretamente o vetor, validando antes que codigo é um índice válido.


Exercício 6 — Quadrado perfeito pela soma de ímpares
#include <stdio.h>

int main() {
    int n, soma, impar;

    printf("Digite um numero inteiro e positivo: ");
    scanf("%d", &n);

    while (n <= 0) { // consistência: insiste até receber um valor válido
        printf("Valor invalido! Digite um numero inteiro e positivo: ");
        scanf("%d", &n);
    }

    soma = 0;
    impar = 1;
    while (soma < n) {
        soma += impar;
        impar += 2; // próximo ímpar: 1, 3, 5, 7, ...
    }

    if (soma == n) {
        printf("%d e quadrado perfeito\n", n);
    } else {
        printf("%d NAO e quadrado perfeito\n", n);
    }

    return 0;
}

Usamos while (soma < n), em vez de um for, porque não sabemos de antemão quantos ímpares serão necessários (Aula 3). Se n for um quadrado perfeito, a soma bate certinho em n ao final de algum passo; caso contrário, a soma ultrapassa n antes de conseguir atingi-lo exatamente (já que os ímpares somados sempre crescem) — por isso a comparação final é soma == n, e não apenas soma >= n.


Exercício 7 — Estoque em múltiplas lojas
#include <stdio.h>

#define NUM_PRODUTOS 50
#define NUM_LOJAS 4

int main() {
    int estoque[NUM_PRODUTOS][NUM_LOJAS];
    int estoqueTotal[NUM_PRODUTOS];
    int p, l;

    // (a) preenche a matriz por leitura
    for (p = 0; p < NUM_PRODUTOS; p++) {
        for (l = 0; l < NUM_LOJAS; l++) {
            printf("Estoque do produto %d na loja %d: ", p, l);
            scanf("%d", &estoque[p][l]);
        }
    }

    // (b) calcula o total de cada produto, somando as lojas
    for (p = 0; p < NUM_PRODUTOS; p++) {
        estoqueTotal[p] = 0;
        for (l = 0; l < NUM_LOJAS; l++) {
            estoqueTotal[p] += estoque[p][l];
        }
    }

    // (c) relatorio de produtos com estoque baixo em alguma loja
    printf("Produtos com estoque baixo:\n");
    for (p = 0; p < NUM_PRODUTOS; p++) {
        for (l = 0; l < NUM_LOJAS; l++) {
            if (estoque[p][l] < 10) {
                printf("Produto %d, loja %d: apenas %d unidades\n", p, l, estoque[p][l]);
            }
        }
    }

    return 0;
}

Note que o item (c) percorre a matriz estoque novamente (e não estoqueTotal): o enunciado pede o estoque baixo em qualquer loja individualmente, não no total somado — por isso é a matriz bidimensional, e não o vetor de totais, que precisa ser verificada célula por célula.


Exercício 8 — Série que converge para n/(n+1)
#include <stdio.h>
#include <math.h>

int main() {
    float n, tolerancia, termo, soma;
    int k;

    printf("Digite n: ");
    scanf("%f", &n);
    printf("Digite a tolerancia: ");
    scanf("%f", &tolerancia);

    soma = 0;
    k = 1;
    do {
        termo = n / ((n + k) * (n + k + 1));
        soma += termo;
        k++;
    } while (fabs(termo) > tolerancia);

    printf("Soma aproximada: %.6f\n", soma);

    return 0;
}

Usamos do-while porque é preciso calcular ao menos um termo antes de poder testar a condição de parada (Aula 3) — não daria para testar fabs(termo) sem primeiro calcular algum termo. A cada volta, um novo termo da série é somado e k avança; o laço para assim que o termo mais recente for pequeno o suficiente (menor ou igual à tolerância).

Compilando com math.h. Funções como fabs e sqrt (Exercício 11) exigem, em alguns sistemas, que a biblioteca matemática seja explicitamente ligada na compilação: gcc -Wall main.c -o programa -lm.

Exercício 9 — Ocorrências de uma string dentro de outra
#include <stdio.h>
#include <string.h>

#define MAXTXT 200
#define MAXPAL 50

int main() {
    char s1[MAXTXT], s2[MAXPAL];
    int tamS1, tamS2, i, j, ocorrencias = 0;
    int igual;

    printf("Digite string s1: ");
    fgets(s1, MAXTXT, stdin);
    s1[strcspn(s1, "\n")] = '\0'; // remove o '\n' do fgets

    printf("Digite string s2: ");
    fgets(s2, MAXPAL, stdin);
    s2[strcspn(s2, "\n")] = '\0';

    tamS1 = strlen(s1);
    tamS2 = strlen(s2);

    // tenta encaixar s2 a partir de cada posição possível de s1
    for (i = 0; i <= tamS1 - tamS2; i++) {
        igual = 1; // flag: assume que bate, até prova em contrário

        for (j = 0; j < tamS2; j++) {
            if (s1[i + j] != s2[j]) {
                igual = 0;
            }
        }

        if (igual) {
            ocorrencias++;
        }
    }

    printf("A string %s ocorre %d vezes\n", s2, ocorrencias);

    return 0;
}

A ideia é "deslizar" s2 por cima de s1, uma posição de cada vez: para cada posição inicial i em s1, comparamos caractere a caractere com s2 (laço interno com a flag igual). O laço externo vai só até tamS1 - tamS2, pois não faz sentido tentar encaixar s2 em uma posição onde ela não caberia inteira dentro de s1.


Exercício 10 — Livro mais vendido em cada loja
#include <stdio.h>

#define L 4
#define T 4

int main() {
    char titulos[T][80];
    int vendas[L][T];
    int loja, i, maiorVenda;

    // leitura dos titulos
    for (i = 0; i < T; i++) {
        printf("Titulo do livro %d: ", i);
        scanf("%s", titulos[i]);
    }

    // leitura das vendas de cada livro em cada loja
    for (loja = 0; loja < L; loja++) {
        for (i = 0; i < T; i++) {
            printf("Vendas do livro %d na loja %d: ", i, loja);
            scanf("%d", &vendas[loja][i]);
        }
    }

    // consultas repetidas ate um codigo invalido de loja
    while (1) {
        printf("Digite o codigo da loja (ou invalido para sair): ");
        scanf("%d", &loja);

        if (loja < 0 || loja >= L) {
            printf("Finalizando sistema de consulta\n");
            break;
        }

        // descobre a maior venda dessa loja
        maiorVenda = vendas[loja][0];
        for (i = 1; i < T; i++) {
            if (vendas[loja][i] > maiorVenda) {
                maiorVenda = vendas[loja][i];
            }
        }

        // imprime todos os titulos empatados na maior venda
        printf("Mais vendido(s) (%d unidades):\n", maiorVenda);
        for (i = 0; i < T; i++) {
            if (vendas[loja][i] == maiorVenda) {
                printf("- %s\n", titulos[i]);
            }
        }
    }

    return 0;
}

Usamos while (1) (um laço que só termina através de um break interno) porque o programa deve consultar indefinidamente, sem um número de repetições conhecido de antemão — a única forma de saber quando parar é observar o próprio código de loja digitado. A busca do "mais vendido" segue o mesmo padrão de maior elemento de um vetor (Aula 4); o segundo laço, que imprime todos os empatados, é necessário porque o enunciado exige mostrar mais de um título caso haja empate na venda máxima.


Exercício 11 — Pontos mais distantes no plano cartesiano
#include <stdio.h>
#include <math.h>

#define N 5

int main() {
    float x[N], y[N];
    int i, j;
    float dx, dy, distancia, maiorDistancia = 0;

    for (i = 0; i < N; i++) {
        printf("Ponto %d - x: ", i);
        scanf("%f", &x[i]);
        printf("Ponto %d - y: ", i);
        scanf("%f", &y[i]);
    }

    // compara cada par de pontos (i, j), sem repetir pares
    for (i = 0; i < N; i++) {
        for (j = i + 1; j < N; j++) {
            dx = x[i] - x[j];
            dy = y[i] - y[j];
            distancia = sqrt(dx * dx + dy * dy);

            if (distancia > maiorDistancia) {
                maiorDistancia = distancia;
            }
        }
    }

    printf("Maior distancia entre dois pontos: %.2f\n", maiorDistancia);

    return 0;
}

O laço interno começa em j = i + 1, e não em j = 0, para comparar cada par de pontos exatamente uma vez (o par (2, 4) é o mesmo que (4, 2), então não faz sentido calculá-lo duas vezes) — o mesmo tipo de cuidado que tomamos, por exemplo, ao inverter um vetor sem vetor auxiliar (Aula 4). A cada par comparado, calculamos a distância euclidiana e atualizamos maiorDistancia sempre que encontramos algo maior, no mesmo padrão de "maior elemento" já visto diversas vezes ao longo da disciplina.