Documente Academic
Documente Profesional
Documente Cultură
Aplicații în C#
Metoda programării
#5 dinamice
Subsecvența comună de
lungime maximă
Obiective:
1. Exersarea metodei programării dinamice prin implementarea unei
probleme specifice:
Problema subsecvenţei comune de lungime maximă a două mulțimi de
numere întregi (LCS – Longest Common Subsequence).
2. Verificarea înţelegerii conceptului de rezolvare prin abordarea unor
probleme de dificultate asemănătoare.
Ghid practic pentru analiza şi proiectarea algoritmilor. Aplicații în C# 95
1 dacă a[i ] b[0]
lung [i,0]
lung [i 1,0] altfel
a/b 7 6 5 2 8 2 8 1 4 7 10
6 0 1 1 1 1 1 1 1 1 1 1
9 0 1 1 1 1 1 1 1 1 1 1
2 0 1 1 2 2 2 2 2 2 2 2
1 0 1 1 2 2 2 2 3 3 3 3
8 0 1 1 2 3 3 3 3 3 3 3
9 0 1 1 2 3 3 3 3 3 3 3
7 1 1 1 2 3 3 3 3 3 4 4
Ghid practic pentru analiza şi proiectarea algoritmilor. Aplicații în C# 97
a/b 7 6 5 2 8 2 8 1 4 7 10
6 0 1 1 1 1 1 1 1 1 1 1
9 0 1 1 1 1 1 1 1 1 1 1
2 0 1 1 2 2 2 2 2 2 2 2
1 0 1 1 2 2 2 2 3 3 3 3
8 0 1 1 2 3 3 3 3 3 3 3
9 0 1 1 2 3 3 3 3 3 3 3
7 1 1 1 2 3 3 3 3 3 4 4
procedura TipăreşteLCS(
1 int[] a, int n,
2 int[] b, int m,
3 int[,] lung)
Pseudocod 4 scrie „Lungimea maximă:”, lung[n-1,m-1]
5 scrie „Subsecvenţa este:”
6 i n-1
7 j m-1
8 cât timp i >= 0 şi j >= 0 execută
9 dacă a[i] == b[j] atunci
10 scrie a[i]
11 i i-1
12 j j-1
13 altfel
14 dacă i-1>=0 şi
15 lung[i,j] == lung[i-1,j] atunci
16 i i-1
17 altfel
18 j j-1
19 ■
20 ■
■
sf.procedură
class SubsecventaCrescatoareComuna
{
/// <summary>
/// Calculeaza matricea de lungimi partiale aferenta procedurii de
/// calcul a subsecventei crescatoare comuna sirurilor a si b.
/// </summary>
public static int[,] LCS(int[] a, int[] b)
{
int n = a.Length, m = b.Length;
int[,] lung = new int[n, m];
lung[0, 0] = (a[0] == b[0]) ? 1 : 0;
for (int j = 1; j < m; j++)
lung[0, j] = (a[0] == b[j]) ? 1 : lung[0, j - 1];
for (int i = 1; i < n; i++)
lung[i, 0] = (a[i] == b[0]) ? 1 : lung[i - 1, 0];
for (int i = 1; i < n; i++)
for (int j = 1; j < m; j++)
lung[i, j] = Math.Max(
(a[i] == b[j]) ? lung[i-1, j-1]+1 : lung[i-1, j-1],
Math.Max(lung[i - 1, j], lung[i, j - 1])
);
return lung;
}
/// <summary>
/// Tipareste subsecventa crescatoare comuna subsirurilor a si b.
/// </summary>
public static void TiparesteLCS(int[] a, int[] b, int[,] lung)
{
List<int> subsecventa = new List<int>();
int i = a.Length – 1, j = b.Length - 1;
while (i >= 0 && j >= 0)
{
if (a[i] == b[j])
{
subsecventa.Add(a[i]);
i--; j--;
}
else
if (i >= 1 && lung[i - 1, j] == lung[i, j]) i--;
else if (j >= 1 && lung[i, j - 1] == lung[i, j]) j--;
}
subsecventa.Reverse();
foreach (int x in subsecventa)
Console.Write(x + ", ");
}
}
100 Ghid practic pentru analiza şi proiectarea algoritmilor. Aplicații în C#
O soluţie simplă de rezolvare constă în aplicarea algoritmului LCS pentru şirul a şi şirul
a inversat. Orice subsecvenţă palindromică va fi o subsecvenţă comună celor două
şiruri iar algoritmul LCS o identifică pe cea mai lungă.
class CelMaiLungPalindrom
{
static void Main(string[] args)
{
new CelMaiLungPalindrom();
}
public CelMaiLungPalindrom()
{
GenerateTests();
}
/// <summary>
/// Genereaza teste pentru problema palindromului
/// </summary>
private void GenerateTests()
{
int[] n = new int[] {
5, 10, 100, 300, 500, 700, 800, 900, 950, 999 };
for (int indexTest = 0; indexTest < n.Length; indexTest++)
{
int[] a = GenerateArray(n[indexTest]);
WriteToFile(a, "input-"+ (indexTest+1).ToString()+".txt");
int start = Environment.TickCount;
using (StreamWriter writer = new StreamWriter(
"output-" + (indexTest + 1).ToString() + ".txt"))
writer.WriteLine(Solve(a));
int end = Environment.TickCount;
Console.WriteLine("time = {0} ms", end - start);
}
}
/// <summary>
/// Genereaza un sir de numere alese la intamplare
/// </summary>
Random random = new Random();
private int[] GenerateArray(int n)
{
int[] a = new int[n];
for (int i = 0; i < n; i++)
a[i] = random.Next(100);
return a;
}
/// <summary>
/// Rezolva problema palindromului
102 Ghid practic pentru analiza şi proiectarea algoritmilor. Aplicații în C#
/// </summary>
private int Solve(int[] a)
{
int[] aReversed = new int[a.Length];
for (int i = 0; i < a.Length; i++)
aReversed[i] = a[a.Length - 1 - i];
return LengthOfLongestPalindrome(a, aReversed);
}
/// <summary>
/// Problema LCS
/// </summary>
private int LengthOfLongestPalindrome(int[] a, int[] b)
{
int n = a.Length;
int[,] length = new int[n, n];
/// <summary>
/// Afiseaza solutia la consola
/// </summary>
private void WriteToConsole(int[] a)
{
for (int i = 0; i < a.Length; i++)
Console.Write("{0}, ", a[i]);
Console.WriteLine();
}
Ghid practic pentru analiza şi proiectarea algoritmilor. Aplicații în C# 103
/// <summary>
/// Afiseaza solutia intr-un fisier
/// </summary>
private void WriteToFile(int[] a, string fileName)
{
using (StreamWriter writer = new StreamWriter(fileName))
{
writer.WriteLine(a.Length);
for (int i = 0; i < a.Length; i++)
writer.Write("{0}{1}", a[i], i==a.Length-1 ? "" : " ");
}
}
/// <summary>
/// Citeste datele problemei dintr-un fisier
/// </summary>
private int[] ReadFromFile(string fileName)
{
int[] a = null;
using (StreamReader reader = new StreamReader(fileName))
{
int n = int.Parse(reader.ReadLine());
string[] values = reader.ReadLine().Trim().Split(' ');
a = new int[n];
for (int i = 0; i < n; i++)
a[i] = int.Parse(values[i]);
}
return a;
}
}
Excursii
Un grup de prieteni povestesc excursiile la care au participat şi mai
ales oraşele pe care le-au vizitat. Fiecare dintre ei precizează în ordine
Problemă oraşele vizitate. În final, ei se hotărăsc să determine care este cel mai
mare număr de oraşe pe care toţi le-au vizat în aceeaşi ordine.
Fişierul de intrare conţine pe prima linie numărul T de teste. Fiecare
test conţine pe prima linie numărul n≤20 de prieteni iar fiecare
dintre cele n linii următoare conţine oraşele vizitate (în ordinea
vizitării lor) separate printr-un spaţiu (fiecare prieten a vizitat un
număr de maxim 100 de oraşe).
104 Ghid practic pentru analiza şi proiectarea algoritmilor. Aplicații în C#
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
namespace Excursii
{
class Program
{
private Random random = new Random();
static void Main(string[] args)
{
new Program();
}
public Program()
{
Solve();
}
private void Solve()
Ghid practic pentru analiza şi proiectarea algoritmilor. Aplicații în C# 105
{
StreamReader reader = null;
StreamWriter writer = null;
try
{
reader = new StreamReader("input.txt");
writer = new StreamWriter("output.txt", false);
int T = int.Parse(reader.ReadLine());
for (int indexTest = 0; indexTest < T; indexTest++)
{
int n = int.Parse(reader.ReadLine());
List<List<string>> cities =
new List<List<string>>();
for (int i = 0; i < n; i++)
cities.Add(new List<string>(
reader.ReadLine().Split(new char[] { ' ' })));
writer.WriteLine(Solve(n, cities));
}
}
catch (Exception exc) { Console.WriteLine(exc.ToString());}
finally
{
if (reader != null) reader.Close();
if (writer != null) writer.Close();
}
}
private int Solve(int n, List<List<string>> cities)
{
List<string> solution = Solve(cities[0], cities[1]);
for (int i = 2; i < n; i++)
solution = Solve(solution, cities[i]);
return solution.Count;
}
int ii = a.Count - 1;
int jj = b.Count - 1;
List<string> cities = new List<string>();
while (!(ii == 0 && jj == 0))
{
if (ii > 0 && jj > 0 && c[ii, jj]==c[ii - 1, jj - 1]+1)
{
cities.Add(a[ii]);
ii--;
jj--;
}
else
if (ii > 0 && c[ii, jj] == c[ii - 1, jj]) ii--;
else
if (ii > 0 && jj == 0 &&
c[ii, jj] == c[ii - 1, jj] + 1) {
cities.Add(a[ii]);
ii--;
}
else
if (jj > 0 && c[ii, jj] == c[ii, jj - 1])
jj--;
else
if (ii == 0 && jj > 0 &&
c[ii, jj] == c[ii, jj - 1] + 1)
{
cities.Add(a[ii]);
jj--;
}
}
if (a[0] == b[0]) cities.Add(a[0]);
cities.Reverse();
return cities;
}
Verificarea vocabularului
Fie un dicţionar alcătuit dintr-un număr de n cuvinte şi fie m cuvinte
introduse de către un utilizator într-un editor text. Realizaţi operaţia de
Problema verificare a vocabularului, corectând cuvintele introduse de utilizator
#2 pe baza celor din dicţionar.
Fişierul de intrare conţine pe prima linie numărul n de cuvinte din
Ghid practic pentru analiza şi proiectarea algoritmilor. Aplicații în C# 109
Turnurile de cuburi
Fie două turnuri construite din n respectiv m cuburi de culori c1i și c2j.
Dintr-un turn putem elimina orice cub de pe orice poziţie, lungimea
Problema turnului diminuându-se cu 1. Determinaţi numărul minim de cuburi ce
#3 trebuie eliminate astfel încât să obţinem două turnuri identice de
aceeaşi înălţime cu cuburi de pe acelaşi nivel având aceiaşi culoare.
Fişierul de intrare conţine numărul de cuburi n din primul turn
respectiv m din al doilea. Următoarea linie conţine descrierea primului
turn: n valori separate prin spaţiu reprezentând culorile cuburilor ce
alcătuiesc turnul. A treia linie din fişier va conţine descrierea celui de-al
doilea turn. Fişierul de ieşire conţine pe prima linie numărul minim de
cuburi care trebuie eliminate din ambele turnuri. Următoarea linie
conţine indicii cuburilor ce vor fi eliminate din primul turn iar a treia
linie indicii cuburilor eliminate din cel de-al doilea turn.
Intrare Ieşire
45 3
200 100 130 100 1
24 200 130 177 100 03
110 Ghid practic pentru analiza şi proiectarea algoritmilor. Aplicații în C#
Excursii (2)
Un grup de prieteni povestesc excursiile în care au participat şi mai ales
oraşele pe care le-au vizitat. Fiecare precizează în ordine oraşele
Problema vizitate. În final, ei vor să afle care este cel mai mare număr de oraşe
#4 pe care fiecare le-a vizat astfel încât numele orașelor să fie în ordine
alfabetică. Fişierul de intrare conţine pe prima linie numărul T de
prieteni (sau de teste). Fiecare test conţine pe două linii oraşele
vizitate. Fişierul de ieşire conţine câte o linie pe test reprezentând
numărul maxim de oraşe vizitate în aceeaşi ordine de toţi prietenii.
Intrare Ieșire
3 2
5 4
Paris Roma Milano Bucuresti Praga 4
6
Roma Amsterdam Milano Praga Sofia
Budapesta
7
Londra Paris Roma Venetia Milano
Barcelona Praga