exercícios resolvidos algoritmo 2

25
Faculdade de Engenharia da Universidade do Porto LICENCIATURA EM ENGENHARIA INFORMÁTICA E COMPUTAÇÃO - INTRODUÇÃO À PROGRAMAÇÃO DE COMPUTADORES II - 1999/2000 - EXERCÍCIOS João Pascoal Faria ([email protected] ) 1. Introdução ao ambiente de programação 1.1. Siga o seguinte guião: Arranque o Microsoft Visual C++ 5.0 a partir de Start Programs Microsoft Visual C++ 5.0 Microsoft Visual C++ 5.0. Crie um novo projecto e espaço de trabalho ("workspace"), com a opção File New ... Projects, fornecendo os seguintes dados: Win32 Console Application Location = c:\temp (ou outro directório com permissão de escrita) Project name = ip2 (ou outro nome à sua escolha) Create new workspace Crie um ficheiro com a opção File New ... File, fornecendo os seguintes dados: C++ Source File File name = c:\temp\ip2\ip2.c (ou outro nome) Add to project Na janela de edição do ficheiro acabado de abrir, introduza o seguinte programa em C: #include <stdio.h> main() { printf("Ola!\n"); return 0; } Crie o executável com a opção Build Build ip2.exe Corra o executável com a opção Build Execute ip2.exe

Upload: pauloh27

Post on 02-Jan-2016

895 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: Exercícios Resolvidos Algoritmo 2

Faculdade de Engenharia da Universidade do Porto

LICENCIATURA EM ENGENHARIA INFORMÁTICA E COMPUTAÇÃO-

INTRODUÇÃO À PROGRAMAÇÃO DE COMPUTADORES II - 1999/2000-

EXERCÍCIOS

João Pascoal Faria ([email protected])

1. Introdução ao ambiente de programação

1.1. Siga o seguinte guião:

Arranque o Microsoft Visual C++ 5.0 a partir de Start Programs Microsoft Visual C++ 5.0 Microsoft Visual C++ 5.0.

Crie um novo projecto e espaço de trabalho ("workspace"), com a opção File New ... Projects, fornecendo os seguintes dados:

Win32 Console ApplicationLocation = c:\temp (ou outro directório com permissão de escrita)Project name = ip2 (ou outro nome à sua escolha) Create new workspace

Crie um ficheiro com a opção File New ... File, fornecendo os seguintes dados:C++ Source FileFile name = c:\temp\ip2\ip2.c (ou outro nome) Add to project

Na janela de edição do ficheiro acabado de abrir, introduza o seguinte programa em C:

#include <stdio.h>

main(){

printf("Ola!\n");return 0;

}

Crie o executável com a opção Build Build ip2.exe

Corra o executável com a opção Build Execute ip2.exe

Corra o executável fora do Visual C++, a partir do Windows Explorer e a partir da MS-DOS Prompt (o executável estará em c:\temp\ip2\debug\ip2.exe)

Experimente introduzir e executar outros programas apresentados nas aulas teóricas.

Explore à sua vontade o ambiente de trabalho do Visual C++!

1.2. No sistema operativo Linux, crie um ficheiro de texto ola.c com o mesmo conteúdo do exercício anterior, usando um editor de texto à sua escolha (emacs, vi, pico, etc.). Compile o programa com o comando:

gcc ola.c -o olaExecute o ficheiro executável ola criado por este comando.

Page 2: Exercícios Resolvidos Algoritmo 2

2. Introdução à programação em C

2.1. [Deitel 2.7] Identifique e corrija os erros em cada uma das instruções seguintes (Nota: pode existir mais do que um erro por instrução):

a) scanf("%d", value);

b) printf("O produto de %d e %d é %d\n", x, y);

c) firstNumber + secondNumber = sumOfNumbers

d) if (number => largest)largest == number;

e) */ Programa para determinar o maior de três inteiros /*

f) Scanf("%d", anInteger);

g) printf("O resto da divisão de %d por %d é \n", x, y, x % y);

h) if (x = y);printf(%d é igual a %d\n", x, y);

i) printf("A soma é %d\n," x + y);

j) Printf("O valor introduzido foi: %d\n, &value);

2.2. [Deitel 2.12] O que aparece escrito no ecrã, se é que é aparece alguma coisa, em resultado da execução de cada uma das seguintes instruções em C? Suponha que x = 2 e y = 3.

a) printf("%d", x);

b) printf("%d", x + x);

c) printf("x=");

d) printf("x=%d", x);

e) printf("%d = %d", x + y, y + x);

f) z = x + y;

g) scanf("%d%d", &x, &y);

h) /* printf("x + y = %d", x + y); */

i) printf("\n");

2.3. [Deitel 2.15] Indique a ordem de avaliação dos operadores em cada uma das seguintes instruções em C, e mostre o valor de x após a execução de cada uma das instruções.

a) x = 7 + 3 * 6 / 2 - 1;

b) x = 2 % 2 + 2 * 2 - 2 / 2;

c) x = (3 * 9 * (3 + (9 * 3 / (3))));

2.4. [Deitel 2.19] Escreva um programa em C que leia 3 inteiros diferentes introduzidos pelo teclado, e apresente no ecrã a soma, a média, o produto, o menor e o maior desses números. Use apenas a forma de selecção simples (sem else) da instrução if. O diálogo deve aparecer no ecrã com o seguinte aspecto:

Page 3: Exercícios Resolvidos Algoritmo 2

Introduza três inteiros diferentes: 13 27 14A soma é 54A média é 18O produto é 4914O menor é 13O maior é 27

3. Introdução à programação estruturada

3.1. [Deitel 3.11] Identifique e corrija os erros em cada uma das alíneas seguintes (Nota: pode existir mais do que um erro por alínea):

a) if (age >= 65);printf("A idade é maior ou igual que 65\n");

elseprintf("A idade é menor que 65\n");

b) int x = 1, total;

while (x <= 10) {total += x;++x;

}

c) While (x <= 100)total += x;++x;

d) while (y > 0) {printf"%d\n", y);++y;

}

3.2. [Deitel 3.13] O que escreve o programa seguinte?

#include <stdio.h>

main(){

int x = 1, total = 0, y;

while (x <= 10) {y = x * x;printf("%d\n", y);total += y;++x;

}

printf("O total é %d\n", total);return 0;

}

3.3. [Deitel 3.31] Determine a saída produzida em cada uma das alíneas seguintes, no caso em que x = 9 e y = 11, e no caso em que x = 11 e y = 9. Note que o compilador de C ignora a "indentação". Note também que o compilador de C associa um else ao if mais próximo, a não ser

Page 4: Exercícios Resolvidos Algoritmo 2

que se indique outra coisa através de chavetas {}. (Sugestão: Comece por aplicar as convenções habituais de "indentação".)

a) if (x < 10)if (y < 10)printf("*****\n");elseprintf("#####\n");printf("$$$$$\n");

b) if (x < 10) {if (y < 10)printf("*****\n");}else {printf("#####\n");printf("$$$$$\n");}

Para cada um dos exercícios seguintes (3.4 a 3.9), deve proceder da seguinte forma:1. Leia o enunciado do problema.2. Formule o algoritmo usando pseudo-código e refinamento sucessivo "top-down".3. Escreva um programa em C.4. Teste, depure e execute o programa em C.

3.4. Escreva um programa em C que leia uma sequência de números inteiros, e determine e imprima a soma, a média, o menor e o maior dos números,

a) supondo que o fim da sequência é indicado pelo valor 0 (que já não faz parte da sequência);

b) supondo que o comprimento da sequência é indicado previamente.

No caso da alínea (a), o programa deve também imprimir o comprimento da sequência.

3.5. [Deitel 3.34] Escreva um programa que leia o comprimento do lado de um quadrado (entre 1 e 20) e escreva no ecrã um quadrado "oco" com asteriscos. Por exemplo, se o comprimento dado for 5, a saída do programa deve ser:

****** ** ** ******

3.6. [Deitel 3.35] Uma capicua é um número ou um texto que se lê da mesma maneira de trás para a frente e da frente para trás. Por exemplo, os seguintes números são capicuas: 12321, 555, 45554 e 11611.

a) Escreva um programa que leia um inteiro de 3 dígitos e determine se é ou não uma capicua. (Sugestão: Use os operadores de divisão e módulo para separar o inteiro nos seus dígitos.)

b) Generalize o programa da alínea (a) por forma a tratar inteiros com qualquer número de dígitos.

Page 5: Exercícios Resolvidos Algoritmo 2

3.7. Escreva um programa que leia duas horas do dia no formato hhmm, em que hh indica as horas (de 0 a 23) e mm os minutos (de 0 a 59), e calcule e imprima a respectiva soma no formato d hhmm, em que d é o número de dias (0 ou 1). Exemplo:

Introduza a primeira hora no formato hhmm: 1845Introduza a segunda hora no formato hhmm : 0620A soma é: 1 0105

Sugestões: Ler cada hora do dia para um inteiro; o quociente e o resto da divisão inteira por 100 dão as horas e os minutos, respectivamente. Para escrever um inteiro n (representando horas ou minutos) em 2 casas preenchidas com zeros à esquerda, usar a instrução:

printf("%.2d",n);

3.8. [Deitel 3.42] Escreva um programa que leia o raio de um círculo (do tipo float) e calcule e imprima o diâmetro, o perímetro e a área do círculo, com 2 casas decimais. Use o valor 3.14159 para .

3.9. [Deitel 3.44] Escreva um programa que leia três valores positivos do tipo float, e determine se esses valores poderiam representar as medidas dos lados de um triângulo. (Sugestão: Não é possível construir um triângulo com um lado maior do que a soma dos outros dois.)

4. Controlo de programa

4.1. [Deitel 4.5 a)-g)] Encontre e corrija os erros em cada uma das alíneas seguintes:

a) For (x = 100, x >= 1, x++)printf("%d\n", x);

b) O código seguinte deve imprimir se um dado inteiro é par ou ímpar:

switch (value % 2) {case 0:

printf("Inteiro par\n");case 1:

printf("Inteiro ímpar\n");}

c) O código seguinte deve ler um inteiro e um caracter e imprimir os dois. Suponha que o utilizador introduz 100 A.

scanf("%d", &intVal);charVal = getchar();printf("Inteiro: %d\nCaracter: %c\n", intVal, charVal);

d) for (x = .000001; x >= .0001; x += .000001)printf("%.7f\n", x);

e) O código seguinte deve imprimir os inteiros ímpares de 999 até 1:

for (x = 999; x >= 1; x += 2)printf("%d\n", x);

Page 6: Exercícios Resolvidos Algoritmo 2

f) O código seguinte deve imprimir os inteiros pares de 2 até 100:

contador = 2;

Do {if (contador % 2 == 0)

printf("%d\n", contador);contador += 2;

} While (contador < 100);

g) O código seguinte deve somar os inteiros de 100 até 150 (suponha que total é inicializado com 0):

for (x = 100; x <= 150; x++);total += x;

h) O programa seguinte pretende responder à questão 3.6.b):

#include <stdio.h>;

main{

int n;

doprintf("Introduza um inteiro positivo: ");scanf("%f", n);

while n > 0;

int aux = n;int m = 0;

dom = m * 10 + aux mod 10

while aux / = 10;

/* Nesta altura, m deve ter os mesmos digitos que n,/* mas por ordem inversa.

if (m = n)printf(n, " é uma capicua\n")

elseprintf(n, " não é uma capicua\n");

return 0}

4.2. [Deitel 4.7] Escreva instruções for para imprimir cada uma das seguintes sequências de valores:

a) 1, 2, 3, 4, 5, 6, 7

b) 3, 8, 13, 18, 23

c) 20, 14, 8, 2, -4, -10

Page 7: Exercícios Resolvidos Algoritmo 2

4.3. Escreva instruções for para calcular a soma dos primeiros n termos (com n inteiro previamente definido) de cada uma das séries seguintes:

a) Série que dá o valor da constante matemática :

44

3

4

5

4

7

4

9

4

11 . . .

b) Série que dá o valor da constante matemática e:

11

1

1

2

1

3

! ! !. . .

(Sugestão: calcule cada termo a partir do termo imediatamente anterior.)

c) Série que dá o valor de e-x (com x real positivo previamente definido):

11 2 3

2 3

x x x

! ! !. . .

(Sugestão: calcule cada termo a partir do termo imediatamente anterior.)

4.4. [Deitel 3.47, 4.14] O factorial de um número n não-negativo é denotado por n! e é definido da seguinte forma:

n! = n (n-1) (n-2) ... 1 (para n 1)n! = 1 (para n =0)

a) Escreva um programa que leia repetidamente números inteiros não-negativos e calcule e imprima o seu factorial, terminando quando é introduzido um número negativo. (Sugestão: utilize um ciclo for dentro de um ciclo while.)

b) Qual é o maior número cujo factorial é calculado correctamente pelo programa da alínea (a)? Porquê? E se, em vez do tipo int, usasse cada um dos seguintes tipos: long, short e char?

c) Escreva um programa que imprima uma tabela de factoriais dos números de 1 a 15. (Sugestão: utilize um único ciclo for.)

4.5. Escreva um programa para determinar as raízes da equação quadrática ax2+bx+c=0, sendo os coeficientes a, b e c fornecidos pelo utilizador. O programa deve indicar se a equação tem 2 raízes reais diferentes, 2 raízes reais iguais ou 2 raízes complexas conjugadas, e os respectivos valores (com 3 casas decimais). Utilize a função sqrt da biblioteca de funções matemáticas para calcular a raiz quadrada de um número em vírgula flutuante. Exemplo:

Introduza os coeficientes: 2.5 -1 16Há 2 raízes complexas conjugadas: 0.200+2.522i e 0.200-2.522i

4.6. O dia da semana correspondente a uma data pode obter-se através da seguinte fórmula:W = [D + (26 * M - 2) / 10 + Y + (Y / 4) + (C / 4) - 2 * C - (1 + L) * (M / 11)] mod 7

em queW - dia da semana: 0=domingo, 1=segunda, etc.M - mês: 1=Março, 2=Abril, ..., 10=Dezembro=10, 11=Janeiro, 12=FevereiroC - dois primeiros dígitos do ano (exemplo: 19)Y - dois últimos dígitos do ano (exemplo: 98)D - dia do mês, a começar em 1L - 1 se o ano for bissexto e 0 se o ano for comum

Nesta fórmula, o operador "/" refere-se ao quociente da divisão inteira. Os anos bissextos são os anos múltiplos de 4 que não são múltiplos de 100, e ainda os anos múltiplos de 400. Por exemplo, os anos 1996 e 2000 são bissextos, enquanto que os anos 1998 e 1900 são comuns.

a) Escreva um programa que calcule e imprima o número de dias de um mês/ano indicado pelo utilizador. (Sugestão: utilize a instrução switch.)

Page 8: Exercícios Resolvidos Algoritmo 2

b) Escreva um programa que mostre no ecrã o calendário de um mês/ano indicado pelo utilizador. Exemplo:

Mês (1 a 12): 2Ano (com 4 dígitos): 1999

Dom Seg Ter Qua Qui Sex Sab 1 2 3 4 5 6 7 8 9 10 11 12 1314 15 16 17 18 19 2021 22 23 24 25 26 2728

4.7. Um número é primo se apenas for divisível pela unidade e por si próprio. Diz-se que um número a é divisível por um número b, se o resto da divisão inteira de a por b for zero.

a) Escreva um programa que leia um número e determine se é primo ou não.

b) Escreva um programa que imprima os 100 primeiros números primos.

c) Escreva um programa que imprima todos os números primos inferiores a 10000.

4.8. Escreva um programa que imprima o número Romano equivalente a um número decimal entre 1 e 1000 introduzido pelo utilizador.

4.9. Restruture o seguinte código por forma a evitar o uso das instruções break e continue:

int n, i;

while (1) {printf("Introduza um inteiro positivo (0 termina): ");scanf("%d", &n);

if (!n)break;

if (n < 0){

printf("Não aceita números negativos!\n");continue;

}

for (i = 2; i * i <= n; i++)if ( ! (n % i) )

break;

if (i * i <= n)printf("%d não é primo!\n",n);

elseprintf("%d é primo!\n",n);

}

Page 9: Exercícios Resolvidos Algoritmo 2

5. Funções

5.1. [Deitel 5.50] Encontre os erros existentes em cada um dos seguintes segmentos de programas e explique como os corrigiria:

a) float cube(float); /* protótipo */...cube(float number) /* definição */{

return number * number * number;}

b) register auto int x = 7;

c) int randomNumber = srand();

d) float y = 123.45678;int x;

x = y;printf("%f\n", (float) x);

e) double square(double number){

double number;

return number * number;}

f) int sum(int n){

if (n == 0)return 0;

elsereturn n + sum(n);

}

5.2. [Deitel 5.11] A função floor pode ser usada para arredondar um número em vírgula flutuante para um certo número de casas decimais. Por exemplo, a instrução:

y = floor(x * 100 + 0.5) / 100;arredonda x para as centésimas (isto é, para 2 casas decimais).Escreva uma função

double round(double x, unsigned n)que arredonda um número em vírgula flutuante (x) para um certo número de casas decimais (n), retornando o valor arredondado. Inclua essa função num programa de teste que pede ao utilizador valores de x e n, e imprime o valor de round(x,n) com um número suficientemente grande de casas decimais, terminando quando o utilizador introduz x=0.

Page 10: Exercícios Resolvidos Algoritmo 2

5.3. [Deitel 5.29, 5.42] O maior divisor comum (mdc) de dois inteiros x e y é o maior inteiro que divide tanto x como y (com resto 0).a) Escreva uma função mdc(x,y) capaz de calcular o maior divisor comum de dois inteiros, seguindo a definição acima.b) É sabido que mdc(x,y)=mdc(y,x mod y) e mdc(x,0)=x. Escreva uma versão recursiva da função mdc(x,y) directamente a partir destas propriedades. 1

c) Desenvolva uma versão não recursiva da função mdc(x,y) tirando partido das propriedades referidas em b).

5.4. Pretende-se implementar uma função double PotenciaNatural(double x, unsigned n)

que retorne o valor de xn, sem recorrer à biblioteca de funções matemáticas.

a) Escreva uma implementação iterativa desta função, usando um ciclo for.

b) Uma implementação directa desta função gasta n-1 multiplicações em vírgula flutuante. No entanto, o número de operações em vírgula flutuante pode ser drasticamente reduzido se atendermos a que:

xn = [xn/2]2, se n fôr par (2); xn = x[x(n-1)/2]2, se n for ímpar (3)x2 = xx; x1 = x; x0 = 1

Por exemplo, x40 pode ser calculado com apenas 6 multiplicações em vírgula flutuante (e mais algumas operações aritméticas com inteiros, muito mais rápidas).Escreva uma implementação recursiva da função PotenciaNatural tirando partido destas fórmulas.

c) Existe uma implementação iterativa (simples) que tire partido das propriedades indicadas na alínea anterior?

5.5. [Deitel 5.31] Escreva um programa que simule o lançamento de uma moeda. Para cada lançamento, o programa deve imprimir Cara ou Coroa. O programa deve efectuar 100 lançamentos, contar o número de vezes que sai cada uma das faces, e imprimir os resultados. O programa principal deve chamar uma função separada cara_ou_coroa, sem argumentos, que retorna 0 para cara e 1 para coroa.

5.6. Refaça o exercício 4.7, escrevendo uma função int primo(int n), utilizável em todas as alíneas, que retorna 1 se n for primo e 0 no caso contrário. Note que, para verificar se n é primo, basta dividir n pelos números de 2 a (int)sqrt(n).

5.7. Refaça o exercício 4.8 (programa para imprimir o número Romano equivalente a um número decimal entre 1 e 1000), dividindo o programa em funções para imprimir os milhares (nada ou M), as centenas (nada, C, ..., ou CM), as dezenas (nada, X, XX, ..., ou XC) e as unidades (nada, I, II, ..., ou IX). Cada uma destas funções deve receber como argumento um número entre 0 e 9 (excepto a função para imprimir os milhares, que só recebe 0 ou 1). Por sua vez, cada uma destas funções deve ser implementada através de uma única chamada a uma função mais genérica com o seguinte protótipo:

void para_romano(int digito, char letra1, char letra5, char letra10);

O 1º argumento recebido por esta função é o dígito decimal, de 0 a 9, para imprimir em romano. Os argumentos seguintes indicam os caracteres a usar, de acordo com o peso do dígito (milhares, centenas, dezenas ou unidades). Por exemplo, para imprimir 6 centenas em romano, bastaria invocar

para_romano(6, 'C', 'D', 'M');

1 Conforme o leitor já deve ter reparado, o itálico é usado para expressões matemáticas, e o Courier bold para código C.

Page 11: Exercícios Resolvidos Algoritmo 2

6. "Arrays"

6.1. [Deitel 6.13, a)-d)] Encontre os erros existentes em cada um dos seguintes segmentos de programas:

a) Suponha que foi definido: char str[3];scanf("%s", str); /* O utilizador introduz ola */

b) Suponha que foi definido: int a[3];printf("%d %d %d\n", a[1], a[2], a[3]);

c) float f[3] = {1.1, 10.01, 100.001, 1000.0001};

d) Suponha que foi definido: double d[2][10];d[1, 9] = 2.345;

6.2. Escreva um programa para gerar várias apostas do totoloto todas diferentes entre si, até um máximo de 20 apostas. O número de apostas a gerar é dado pelo utilizador. (Sugestão: Represente cada aposta por um vector de 6 números crescentes (compreendidos entre 1 e 49), e represente o conjunto de apostas por um vector de até 20 apostas.)

6.3. Escreva um programa para imprimir o triângulo de Pascal de altura 14, isto é, os valores de nCk

para 0kn13. O programa deve produzir um resultado do seguinte tipo (os cabeçalhos de linhas e colunas são dispensáveis):

n\k* 0 1 2 3 4 5 6 7 8 9 10 11 12 13

*************************************************************************** 0 * 1 1 * 1 1 2 * 1 2 1 3 * 1 3 3 1 4 * 1 4 6 4 1 5 * 1 5 10 10 5 1 6 * 1 6 15 20 15 6 1 7 * 1 7 21 35 35 21 7 1 8 * 1 8 28 56 70 56 28 8 1 9 * 1 9 36 84 126 126 84 36 9 1 10 * 1 10 45 120 210 252 210 120 45 10 1 11 * 1 11 55 165 330 462 462 330 165 55 11 1 12 * 1 12 66 220 495 792 924 792 495 220 66 12 1 13 * 1 13 78 286 715 1287 1716 1716 1287 715 286 78 13 1

(Sugestão: Utilize um vector para guardar os valores da linha corrente. Para calcular a linha seguinte (n), basta acrescentar um 1 na coluna n e somar ao elemento da coluna k o elemento da coluna k-1, para k=n-1, ..., 1.)

6.4. Pretende-se escrever uma programa para efectuar operações com polinómios. Cada polinómio será representado por um vector com os coeficientes em vírgula flutuante e por um inteiro sem sinal com o grau do polinómio. O programa aceitará polinómios com grau desde 0 até 20.

a) Escreva uma função int lePolinomio(float coefs[]) para ler um polinómio. A função deve colocar os coeficientes no vector coefs e deve retornar o grau do polinómio.

Page 12: Exercícios Resolvidos Algoritmo 2

b) Escreva uma função void escrevePolinomio(int grau, const float coeficientes[]), para escrever um polinómio.

c) Escreva uma função float calculaPolinomio(int grau, const float coefs[], float x), para calcular e retornar o valor de um polinómio num ponto x.(Sugestão: Para minimizar o número de operações em vírgula flutuante, atenda a que, por exemplo, a0+a1x+a2x2+a3x3 = a0+(a1+(a2+(a3)x)x)x. Esta segunda expressão pode ser calculada "de dentro para fora".)

d) Escreva um programa para integrar e testar as funções anteriores, apresentando um menu do tipo:

1. Introduzir polinómio2. Mostrar polinómio3. Calcular valor de polinómio num ponto4. Terminar

6.5. Escreva uma função void factoresPrimos(unsigned n, unsigned factores[32]) para decompor um número inteiro positivo n, entre 2 e 232-1, em factores primos. Os factores primos são colocados no vector factores[], por ordem não decrescente, seguidos de um 0 no fim. Use depois esta função para achar o mmc (menor múltiplo comum) e o mdc (maior divisor comum) de pares de números dados pelo utilizador. No 1º caso é necessário achar a "reunião" dos factores primos e no 2º caso é necessário achar a "intersecção" dos factores primos.

6.6. Pretende-se construir um programa que permite efectuar operações sobre matrizes quadradas.

a) Escreva funções para ler e escrever uma matriz.

b) Escreva uma função para transpor uma matriz.

c) Escreva uma função para multiplicar duas matrizes e colocar o resultado numa terceira.

d) Diz-se que um elemento de uma matriz é um máximo local se for superior a todos os seus vizinhos. Escreva um função maximos_locais que imprima todos os máximos locais e respectivas posições, considerando que qualquer elemento da matriz pode ser um máximo local.

Escreva um programa com um pequeno menu que permita integrar e testar todas as funções escritas nas alíneas anteriores.

6.7. Pretende-se escrever um programa para jogar mastermind (sem visual gráfico). As cores são representadas por números de 1 a 8. A chave é uma sequência de 5 cores diferentes. A chave é gerada aleatoriamente pelo computador, e o utilizador tenta adivinhar a chave. A cada tentativa do utilizador, o computador responde com a respectiva pontuação (número de cores certas no sítio certo e número de cores certas no sítio errado). O computador deve ainda assinalar as tentativas inconsistentes com as pontuações obtidas nas tentativas anteriores (uma tentativa é inconsistente se as pontuações anteriores se alterarem no caso da chave ser substituída por essa tentativa). O número máximo de tentativas é 10.

6.8. [Baseado em Deitel 6.26] O problema das oito rainhas pode ser formulado da seguinte forma: é possível colocar oito rainhas num tabuleiro de xadrez sem se atacarem mutuamente (i.e., sem que nenhumas duas rainhas estejam na mesma linha, na mesma coluna ou na mesma diagonal)? Escreva um programa para achar pelo menos uma solução (ou todas as soluções) para este problema.

Sugestão: Considere as linhas e colunas de um tabuleiro de xadrez numeradas de 0 a 7, e as diagonais numeradas de 0 a 14. Sendo i o nº da linha e j o nº da coluna, as duas diagonais correspondentes podem ser numeradas i+j e i+7-j. Para representar uma solução do problema, use um vector int S[8], em que S[i] indica a coluna (entre 0 e 7) em que se encontra a rainha que se encontra na linha i (entre 0 e 7). Esta representação impede que duas rainhas estejam na mesma linha. Suponha que este vector é preenchido com os números 0, 1, ..., 7 (o que quer dizer que a rainha da linha 0 está na coluna 0, a rainha da linha 1 está na coluna 1, etc.). Desta forma, não há duas rainhas na mesma coluna. No entanto, estão todas as rainhas na mesma diagonal! O problema inicial pode agora ser formulado da seguinte forma: encontrar uma permutação deste vector de 8 números em que não fiquem duas rainhas

Page 13: Exercícios Resolvidos Algoritmo 2

na mesma diagonal. Por exemplo, uma solução possível é {3, 7, 0, 2, 5, 1, 6, 4}. Note que há 8! = 40320 permutações possíveis. Utilize vectores para marcar as diagonais ocupadas.

7. Apontadores

7.1. O que imprime cada um dos seguintes segmentos de programa?

a) int x = 1, y, *p1, *p2;p1 = &x;p2 = &y;*p2 = *p1;++*p1;printf("%d %d %d %d\n", x, y, *p1, *p2);

b) float x = 3.14, *p = &x;*p = x;printf("%.2f %.2f\n", x, *p);

c) int v[] = {1, 2, 3, 5, 7, 11, 13, 17, 19, 0}, *p;for (p = v; *p; p++)

printf("%d\n", *p);

d) int a[4] = {0, 1, 2, 3}, *p;p = a;printf("%d %d %d %d ", a[0], p[1], *(p+2), *(a+3));

e) char *nomes[] = {"João", "José", "Joaquim"};printf("%d %d\n", sizeof(nomes), sizeof(nomes) / sizeof(char *));printf("%c%s %s %s\n", **nomes, nomes[0]+1, *(nomes+1), nomes[2]);

f) void forall(int *v, int size, void (*f)(int)){

for ( ; size>0; size--)(*f)(*v++);

}

void mostra(int n){

printf("%5.2f%%\n", n / 100.0);}

main(){

int v[4] = {57, 63, 80, 90};forall(v, 4, mostra);return 0;

}

7.2. [Deitel 7.6, a)-g)] Indique e corrija, se possível, o erro existente em cada um dos seguintes segmentos de programa.

a) int *numero;printf("%d\n", *numero);

Page 14: Exercícios Resolvidos Algoritmo 2

b) float *realPtr;long *intPtr;intPtr = realPtr;

c) int *x, y;x = y;

d) char s[] = "isto é um array de caracteres";int contador;for ( ; *s != '\0'; s++)

printf("%c ", *s);

e) short *numPtr, resultado;void *genericPtr = numPtr;resultado = *genericPtr + 7;

f) float x = 19.34;float xPtr = &x;printf("%f\n", xPtr);

g) char *s;printf("%s\n", s);

h) char s[] = "ola";const char * p1 = s;char * const p2 = s;p1[2] = 'e';p2[2] = 'e';

7.3. [Wirth] O Quicksort (também designado método de ordenação por partição) é um método de ordenação de arrays muito eficiente que se baseia num algoritmo recursivo do seguinte tipo:

1. Passo de partição:1.1. Escolher um elemento arbitrário (x) do array (v) não ordenado, por exemplo o do meio.1.2. Partir o array em dois sub-arrays, com valores x do lado esquerdo e valores x do lado

direito, da seguinte forma:1.2.1. Inicializar i = primeira posição do array1.2.2. Inicializar j = última posição do array1.2.3. Enquanto i j fazer:

1.2.3.1. Enquanto v[i] < x, incrementar i 1.2.3.2. Enquanto v[j] > x, decrementar j 1.2.3.3. Se i j, trocar v[i] com v[j], incrementar i e decrementar j

2. Passo recursivo: Efectuar o passo 1 para cada um dos sub-arrays (esquerdo e direito) não ordenados, desde que o seu comprimento seja maior do que 1, ou seja:2.1. Se j > primeira posição do array, ordenar o sub-array entre a primeira posição e a posição j,

inclusivé.2.2. Se i < última posição do array, ordenar sub-array entre a posição i e a última posição,

inclusivé.

A figura seguinte ilustra o passo 1 do algoritmo.

Page 15: Exercícios Resolvidos Algoritmo 2

a) Escreva uma função recursiva quicksort para ordenar um array de inteiros pelo método exposto, recebendo como argumentos o endereço do primeiro elemento e o número de elementos do array a ordenar. A função deve ter o seguinte protótipo:

void quicksort (int *v, int size);

b) Escreva um pequeno programa para testar a função anterior.

c) Escreva e teste uma versão alternativa da função quicksort, recebendo como argumentos os endereços do primeiro e do último elemento do array a ordenar.

7.4. [adaptado de Deitel 7.25] A seguinte matriz de 0's e 1's representa um labirinto de 8 x 8 (com o trajecto entre a entrada e a saída realçado a bold):

1 1 1 1 1 1 1 1 0 0 0 0 0 1 1 1

1 0 1 1 0 1 0 0 1 0 1 1 1 1 0 1 1 0 1 0 0 0 0 11 0 1 0 1 1 1 11 0 0 0 0 0 0 11 1 1 1 1 1 1 1

Os 1's representam as paredes. Um método simples para encontrar a saída garantidamente (não necessariamente pelo caminho mais curto), consiste em "seguir sempre pela direita". Se o labirinto não tiver nenhuma saída, acaba por se voltar à posição de partida.

a) Escreva uma função atravessaLabirinto para atravessar um labirinto 8 x 8. A função deve receber como argumentos a matriz de caracteres que descreve o labirinto, bem como a linha e coluna de entrada (na fronteira do labirinto). A função deve substituir 0 por X ao longo do caminho percorrido, e deve marcar a entrada com E e a saída com S (caso exista saída).

b) Escreva uma função geraLabirinto para gerar aleatoriamente um labirinto 8 x 8 (não necessariamente com saída), tendo como argumentos a matriz de caracteres a ser preenchida com 0's e 1's, e apontadores para receber a linha e coluna de entrada.

c) Escreva uma função imprimeLabirinto para imprimir um labirinto. A função deve receber como argumento a matriz de caracteres que descreve o labirinto.

d) Escreva um programa para integrar e testar as funções anteriores.

e) Generalizar as funções anteriores (acrescentado ou modificando argumentos se necessário) para trabalhar com labirintos de qualquer dimensão, não necessariamente quadrados. (Sugestão: Atenda a que, numa matriz com m colunas, o elemento da coluna j da linha i tem um offset de m*i+j posições em relação ao elemento da 1ª coluna da 1ª linha.)

7.5. Escreva uma função void numExtenso(unsigned n), para escrever no ecrã um número n, entre 0 e 9999, por extenso. Por exemplo, numExtenso(2432) deve produzir:

DOIS MIL QUATROCENTOS E TRINTA E DOIS

Sugestão: Utilize arrays de strings para guardar as designações das unidades, dezenas e centenas por extenso:

char *unidades [] = {"ZERO", "UM" ,"DOIS" , ... ,"DEZANOVE"};

char *dezenas [] = {"", "DEZ", "VINTE", ... , "NOVENTA"};char *centenas [] = {"", "CEM", "DUZENTOS", ... , "NOVECENTOS"};

7.6. Usando a função do problema anterior, escreva uma função void dataExtenso(int dia, int mes, int ano), para escrever no ecrã uma data por extenso. Por exemplo, dataExtenso(27,4,1998) deve produzir:

Page 16: Exercícios Resolvidos Algoritmo 2

VINTE E SETE DE MARÇO DE MIL NOVECENTOS E NOVENTA E OITO

Escreva um pequeno programa para testar esta função.

Sugestão: Utilize um array de strings para guardar as designações dos meses por extenso:

char *meses[] = {"", "JANEIRO", "FEVEREIRO", ..., "DEZEMBRO"};

8. Caracteres e cadeias de caracteres (strings)

8.1. Escreva um função int identificadorValido(char *str) que verifica se uma string (str) contém um identificador válido, isto é, uma letra seguida de zero ou mais letras ou dígitos. (Sugestão: Use as funções isalpha e isalnum.)

8.2. Escreva uma função double converteMontante(char *str) para converter uma string com um montante em dinheiro para double. Na string, é usado o cifrão ($) para separar os escudos dos tostões e centavos e é usado o ponto (.) para separar grupos de 3 dígitos. O montante pode ser precedido do sinal menos (-). Exemplos: -1.000$00 67.718$30 14$50 . (Sugestão: Use a função isdigit.)

8.3. Escreva uma função void formataMontante(double montante, char *str) com o objectivo oposto da função anterior.

8.4. Escreva uma função void normalizaNome(char *nomePtr, char *normPtr) para normalizar um nome (apontado por nomePtr) da seguinte forma: as minúsculas são convertidas para maiúsculas; sequências de vários espaços ou tabs entre duas palavras são substituídos por um único espaço; espaços ou tabs no início ou no fim são suprimidos; são removidas as palavras "DE", "DO", "DA", "DAS", "DOS" e "E". O nome normalizado é colocado no array de caracteres apontado por normPtr. Considere que cada palavra do nome não tem mais de 40 caracteres. (Sugestão: Guarde as palavras a eliminar num array de strings e use as funções strtok, strcmp, toupper e strcat.)

8.5. Escreva um programa para ler nomes do "standard input" (um nome em cada linha, com 256 caracteres no máximo) e imprimir os nomes normalizados correspondentes no "standard ouput", usando a função do problema anterior. O programa deve terminar quando aparece uma linha vazia. (Sugestão: Use as funções gets e puts.)

8.6. Escreva uma função int contaOcorrências(char *s1, char *s2) para contar o número de ocorrências não sobrepostas da string s1 na string s2. (Sugestão: Use a função strstr.)

8.7. Refaça a função numExtenso() do exercício 7.5, por forma a escrever o número por extenso numa string, em vez de o escrever no ecrã. O protótipo da novo função será:

void numExtenso(unsigned n, char *str) ;(Sugestão: Use a função strcat.)

8.8. Escreva uma variante da função quicksort do exercício 7.3 (ver Resposta a exercícios seleccionados), para ordenar um array de strings em vez de um array de inteiros. Usando essa variante,

Page 17: Exercícios Resolvidos Algoritmo 2

escreva um programa que leia várias linhas do "standard input", até um máximo de 50 linhas de 80 caracteres, e escreva as mesmas linhas ordenadas alfabeticamente no "standard output".

8.9. Escreva um programa que copie caracteres do "standard input" para o "standard output", suprimindo as sequências de caracteres começadas em "/*" e terminadas em "*/" (início e fim de comentário em C), bem como as sequências de caracteres começadas em "//" e terminadas com o caracter de mudança de linha. (Sugestão: Use as funções getchar e putchar.)

9. Estruturas, uniões, enumerações e manipulação ao bit

9.1. Suponha que se define o seguinte tipo de estrutura para representar datas:

struct data {int dia, mes, ano;

};

Implementa e teste as seguintes funções:

/* Lê uma data do "standard input", no formato dia/mês/ano, com o mês de 1 a 12 e o ano com 4 dígitos. Retorna a estrutura que descreve a data. */struct data le_data(void);

/* Escreve uma data no "standard output", no mesmo formato entendido por "le_data". */void escreve_data(struct data d);

/* Compara duas datas. Retorna 1, 0 ou –1 conforme a 1ª data (d1) é maior, igual ou menor do que a 2ª data (d2). */int compara_datas(struct data d1, struct data d2);

9.2. Suponha que, para implementar uma calculadora capaz de trabalhar com números inteiros ou em vírgula flutuante, se definem os seguintes tipos de dados e protótipos de funções:

enum number_types {LONG, DOUBLE};

union number_value {long l_value; double d_value;

};

struct number {enum number_types type; /* indica qual o campo usado na união

*/union number_value value;

};

typedef struct number NUMBER;

/* Lê um número do "standard input". Se o número for introduzido com o ponto decimal ou com o "E" da notação exponencial, lê-o para um double. Senão, lê-o para um long. Retorna a estrutura com o tipo e o valor lido. */NUMBER read_number(void);

Page 18: Exercícios Resolvidos Algoritmo 2

/* Escreve o número descrito por "num" no "standard output", usando a notação apropriada ao tipo. */void write_number(NUMBER num);

/* Soma dois números inteiros ou em vírgula flutuante e retorna a estrutura com o tipo e o valor resultante. Se pelo menos um dos números for do tipo double, o resultado é do tipo double. Senão, o resultado é do tipo long. */NUMBER add_numbers(NUMBER num1, NUMBER num2);

a) Implementa e teste as funções anteriores.

b) Implementa versões alternativas das funções anteriores, usando apontadores para estruturas em vez de estruturas na chamada das funções. Os protótipos passam a ser:

void read_number(NUMBER *numPtr);

void write_number(const NUMBER *numPtr);

void add_numbers(const NUMBER *numPtr1, const NUMBER *numPtr2, NUMBER *sumPtr);

9.3. Refaça o exercício 6.8 (ver exercícios resolvidos), usando dois inteiros, em vez de dois vectores, e operações bit-a-bit para tratar as duas diagonais. Comece por escrever instruções ou expressões para activar (colocar a 1), desactivar (colocar a 0) e testar (retornando 1 ou 0) o i-ésimo bit de uma variável a do tipo unsigned, com i entre 0 e 31. Escreva também uma instrução para desactivar todos os bits.

9.4. A assiduidade de um funcionário num mês pode ser codificada num inteiro de 32 bits sem sinal (unsigned), em que o i-ésimo bit a 1 (0) significa que o funcionário compareceu (faltou) ao trabalho no dia i (1in.º de dias do mês). Implemente e teste as seguintes funções, baseadas nessa representação:

/* Lê, codifica e retorna os dias de comparência ao trabalho de um dado funcionário num dado mês. */unsigned le_dias_trab(char *nome_func, char *mes, int n_dias_mes);

/* Imprime os dias de comparência ao trabalho de um dado funcionário num dado mês. */void escreve_dias_trab(char *nome_func, char *mes, unsigned dias_trab);

/* Conta os dias de comparência ao trabalho de um dado funcionário num dado mês (irrelevantes para o caso). */int conta_dias_trab(unsigned dias_trab);

10. Manipulação de ficheiros

10.1. Suponha que as notas obtidas pelos alunos a um teste se encontram num ficheiro de texto "notas.dat". A 1ª linha do ficheiro tem o número de alunos que compareceram ao teste. Cada um das restantes linhas do ficheiro tem o nome do aluno e a nota respectiva, separados por um ou mais espaços ou "tabs". A nota é indicada por um inteiro entre 0 e 20.A partir dessas notas, pretende-se construir um ficheiro de texto "histograma.dat", com o número de ocorrências de cada nota (0, 1, 2, ..., 20). Cada linha do ficheiro tem a nota e o número de alunos com essa nota, separados por um espaço.

Page 19: Exercícios Resolvidos Algoritmo 2

Escreva um programa que construa o ficheiro "histograma.dat" a partir do ficheiro "notas.dat". Por uma questão de generalidade, o programa deve perguntar ao utilizador os nomes dos ficheiros no início da sua execução.

10.2. Escreva um programa para efectuar a manutenção de um ficheiro binário "artigos.dat" com dados de artigos em stock. O ficheiro é constituído por registos do seguinte tipo:

struct artigo{

char nome_artigo [40]; /* string */float quantidade_em_stock;char unidade_medida [10]; /* string */float preco_unitario;

};

O programa deve começar por abrir o ficheiro. No caso do ficheiro não existir, o programa deve criar o ficheiro vazio. Seguidamente, deve apresentar um menu com as seguintes opções:

1. Listar o conteúdo do ficheiro2. Inserir um novo artigo no fim do ficheiro3. Procurar um artigo pelo nome4. Procurar um artigo pela posição no ficheiro5. Modificar os dados do último artigo procurado6. Terminar

Antes de terminar, o programa deve fechar o ficheiro.

11. Estruturas de dados dinâmicas

11.1. Escreva um programa que lê um conjunto de linhas do standard input e as escreve de volta no standard output por ordem inversa. Suponha que as linhas de entrada não têm mais de 1000 caracteres. As linhas de entrada devem ser guardadas em memória numa lista ligada usada como pilha ("last in first out"). Cada nó da lista deve conter dois campos: um apontador para o nó seguinte; um apontador para uma string (array de caracteres terminado em '\0') alocada dinâmicamente contendo uma linha lida do standard input.Sugestão: Use as funções gets(s) e puts(s). A função gets retorna NULL quando encontra o fim dos dados de entrada (EOF). Quando se introduzem dados pelo teclado, o fim dos dados é geralmente sinalizado com Ctrl-D em UNIX e com Ctrl-Z em DOS/WINDOWS.

11.2. (baseado em 10.2) Escreva um programa para efectuar a manutenção de uma lista de artigos. Cada artigo é descrito por uma estrutura do seguinte tipo:

struct artigo{

char nome_artigo [40]; /* string */float quantidade_em_stock;char unidade_medida [10]; /* string */float preco_unitario;

};

A lista de artigos deve ser representada em memória por uma lista ligada. Cada nó da lista deve ter uma estrutura do tipo indicado acima e um apontador para o nó seguinte. A lista deve estar ordenada por

Page 20: Exercícios Resolvidos Algoritmo 2

nome de artigo. Não podem existir dois artigos com o mesmo nome. No ínico do programa a lista está vazia. O programa deve apresentar um menu com as seguintes opções:

7. Listar (imprime o conteúdo da lista em formato tabular)8. Inserir um artigo (pergunta os dados do artigo e insere na lista)9. Eliminar um artigo pelo nome (pergunta o nome do artigo, mostra os dados actuais,

pede confirmação e só depois o elimina da lista)10. Modificar um artigo (mantendo o nome) (pergunta o nome do artigo, mostra os

dados actuais e pede os novos dados)11. Procurar um artigo pelo nome (pergunta o nome do artigo e mostra os respectivos

dados)12. Terminar

Antes de terminar, o programa deve libertar a memória ocupada pela lista.