0% found this document useful (0 votes)
0 views

Assignment_2

This document is a digital assignment submitted by Raghav Kohli for a Design and Analysis of Algorithms course, detailing various algorithm implementations. It includes solutions for the N-Queens problem, Matrix Chain Multiplication, Longest Common Subsequence, 0-1 Knapsack, Assembly Line Scheduling, and Job Sequencing Problem using branch and bound. Each section provides an approach, code implementation, and test cases to validate the algorithms.

Uploaded by

Raghav Kohli
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
0 views

Assignment_2

This document is a digital assignment submitted by Raghav Kohli for a Design and Analysis of Algorithms course, detailing various algorithm implementations. It includes solutions for the N-Queens problem, Matrix Chain Multiplication, Longest Common Subsequence, 0-1 Knapsack, Assembly Line Scheduling, and Job Sequencing Problem using branch and bound. Each section provides an approach, code implementation, and test cases to validate the algorithms.

Uploaded by

Raghav Kohli
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 26

Design and Analysis of Algorithm DIGITAL

ASSIGNMENT

Submitted by

Raghav Kohli
Reg.no: 23BCI0219
Branch: Bachelor of Technology in Computer Science and Engineering(With specialization)
School: SCOPE

Under the guidance of

Prof. Shalini L

VIT, Vellore
DAA Lab assignment 2
(Note: The questions are not in the order of the question sheet)
Q1. Write a program to implement N-Queens problem

Approach:

Use backtracking to place queens one by one in each row, ensuring no two attack each other. If a valid position is
found, recursively place the next queen; otherwise, backtrack and try the next possibility.

Code:

#include <iostream>

#include <vector>
using namespace std;

void printBoard(vector<vector<int>> &board, int N)


{
for (int i = 0; i < N; i++)
{
for (int j = 0; j < N; j++)
{
cout << (board[i][j] ? "Q " : "- ");
}
cout << endl;
}
cout << endl;
}

bool isSafe(vector<vector<int>> &board, int row, int col, int N)


{
for (int i = 0; i < col; i++)
{
if (board[row][i])
return false;
}

for (int i = row, j = col; i >= 0 && j >= 0; i--, j--)


{
if (board[i][j])
return false;
}

for (int i = row, j = col; i < N && j >= 0; i++, j--)


{
if (board[i][j])
return false;
}

return true;
}

int countSolutions = 0;
bool solveNQueens(vector<vector<int>> &board, int col, int N)
{
if (col >= N)
{
countSolutions++;
printBoard(board, N);
return true;
}

bool res = false;


for (int i = 0; i < N; i++)
{
if (isSafe(board, i, col, N))
{
board[i][col] = 1;
res = solveNQueens(board, col + 1, N) || res;
board[i][col] = 0;
}
}

return res;
}

int main()
{
int N;
cout << "Enter the number of queens: ";
cin >> N;

vector<vector<int>> board(N, vector<int>(N, 0));

solveNQueens(board, 0, N);

cout << "Total solutions: " << countSolutions << endl;

return 0;
}
Pseudocode:
Output:

Test case 01:

Test Case 02:


Test Case 03:
Q3- (ii): Matrix Chain Multiplication

Approach:

Use Dynamic Programming (DP) with Memoization to find the optimal way to parenthesize matrices to minimize
multiplication cost. Define dp[i][j] as the minimum cost to multiply matrices from index i to j, and compute it using
the recurrence:
Code:

#include <iostream>
#include <vector>
#include <limits.h>

using namespace std;

int matrixChainOrder(vector<int> &p, int n)


{
vector<vector<int>> dp(n, vector<int>(n, 0));

for (int len = 2; len < n; len++)


{
for (int i = 1; i < n - len + 1; i++)
{
int j = i + len - 1;
dp[i][j] = INT_MAX;
for (int k = i; k < j; k++)
{
int cost = dp[i][k] + dp[k + 1][j] + p[i - 1] * p[k] * p[j];
dp[i][j] = min(dp[i][j], cost);
}
}
}
return dp[1][n - 1];
}

int main()
{
int n;
cout << "Enter number of matrices: ";
cin >> n;
vector<int> p(n + 1);

cout << "Enter the dimensions: ";


for (int i = 0; i <= n; i++)
{
cin >> p[i];
}

cout << "Minimum multiplication cost: " << matrixChainOrder(p, n + 1) << endl;

return 0;
}
Pseudocode:
Output:

Test case 1 – Normal case

Test case 2- If all the matrices have same dimensions


Q3- (iii) Longest Common Subsequence

Approach:

Use Dynamic Programming (DP) with a 2D table to compute the Longest Common Subsequence (LCS) length. Define
dp[i][j] as the length of the LCS of prefixes X[0...i-1] and Y[0...j-1], using the recurrence:

Code:

#include <iostream>
#include <vector>

using namespace std;

string lcs(string X, string Y)


{
int m = X.length(), n = Y.length();
vector<vector<int>> dp(m + 1, vector<int>(n + 1, 0));

// Fill the DP table


for (int i = 1; i <= m; i++)
{
for (int j = 1; j <= n; j++)
{
if (X[i - 1] == Y[j - 1])
dp[i][j] = dp[i - 1][j - 1] + 1;
else
dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
}
}

// Reconstruct the LCS string


int index = dp[m][n];
string lcsStr(index, ' ');
int i = m, j = n;

while (i > 0 && j > 0)


{
if (X[i - 1] == Y[j - 1])
{
lcsStr[--index] = X[i - 1];
i--;
j--;
}
else if (dp[i - 1][j] > dp[i][j - 1])
{
i--;
}
else
{
j--;
}
}

return lcsStr;
}

int main()
{
string X, Y;
cout << "Enter first string: ";
cin >> X;
cout << "Enter second string: ";
cin >> Y;

string result = lcs(X, Y);


cout << "Length of LCS: " << result.length() << endl;
cout << "LCS: " << result << endl;
return 0;
}

Pseudocode:
Output:
Test case 01- Normal case

Test case 2- No common subsequence

Test case -03 Entire string is LCS


Test case 04- Multiple common substring – Lexicographically first

Q3-(iv): 0-1 knapsack

Approach:

Use Dynamic Programming (DP) with a 2D table to solve the 0/1 Knapsack Problem. Define dp[i][w] as the
maximum value that can be obtained using the first i items with a weight limit w, using the recurrence:

Code:

#include <iostream>
#include <vector>

using namespace std;

int knapsack(int W, vector<int> &weights, vector<int> &values, int n)


{
vector<vector<int>> dp(n + 1, vector<int>(W + 1, 0));

for (int i = 1; i <= n; i++)


{
for (int w = 1; w <= W; w++)
{
if (weights[i - 1] <= w)
dp[i][w] = max(values[i - 1] + dp[i - 1][w - weights[i - 1]], dp[i
- 1][w]);
else
dp[i][w] = dp[i - 1][w];
}
}
return dp[n][W];
}

int main()
{
int n, W;
cout << "Enter number of items: ";
cin >> n;
vector<int> values(n), weights(n);

cout << "Enter weights and values: ";


for (int i = 0; i < n; i++)
{
cin >> weights[i] >> values[i];
}

cout << "Enter knapsack capacity: ";


cin >> W;

cout << "Maximum value in knapsack: " << knapsack(W, weights, values, n) <<
endl;
return 0;
}

Pseudocode:
Output:

Test case 01- general case

Test Case 02- Exact fit


Test Case 03- Single item fits

Test case 04: All items too heavy

Test case 05- Zero knapsack capacity


Q3-(i): ALS (Assembly Line Scheduling)

Approach:

Use Dynamic Programming (DP) with two arrays to compute the minimum time to assemble a product. Define
T1[i] and T2[i] as the minimum time to reach station i on line 1 and line 2, respectively. Use the recurrence:

where a1[i] and a2[i] are processing times, and t1[i-1], t2[i-1] are transfer times. Compute results
iteratively and return min(T1[n-1] + x1, T2[n-1] + x2), where x1 and x2 are exit times.

Code:

Code:
#include <bits/stdc++.h>

using namespace std;

int min(int a, int b)


{
return a < b ? a : b;
}

int ALS(vector<vector<int>> &a, vector<vector<int>> &t, vector<int> &e,


vector<int> &x, int num_stations)
{
vector<int> T1(num_stations), T2(num_stations);

T1[0] = e[0] + a[0][0];


T2[0] = e[1] + a[1][0];

for (int i = 1; i < num_stations; ++i)


{
T1[i] = min(T1[i - 1] + a[0][i], T2[i - 1] + t[1][i - 1] + a[0][i]);
T2[i] = min(T2[i - 1] + a[1][i], T1[i - 1] + t[0][i - 1] + a[1][i]);
}

return min(T1[num_stations - 1] + x[0], T2[num_stations - 1] + x[1]);


}

int main()
{
int num_stations;
cout << "Enter the number of stations: ";
cin >> num_stations;

vector<vector<int>> a(2, vector<int>(num_stations));


vector<vector<int>> t(2, vector<int>(num_stations - 1));
vector<int> e(2), x(2);

cout << "Enter processing times for line 1: ";


for (int i = 0; i < num_stations; i++)
cin >> a[0][i];

cout << "Enter processing times for line 2: ";


for (int i = 0; i < num_stations; i++)
cin >> a[1][i];

cout << "Enter transfer times from line 1 to line 2: ";


for (int i = 0; i < num_stations - 1; i++)
cin >> t[0][i];

cout << "Enter transfer times from line 2 to line 1: ";


for (int i = 0; i < num_stations - 1; i++)
cin >> t[1][i];

cout << "Enter entry times for both lines: ";


cin >> e[0] >> e[1];

cout << "Enter exit times for both lines: ";


cin >> x[0] >> x[1];

cout << "Minimum time to leave the assembly line: " << ALS(a, t, e, x,
num_stations) << endl;

return 0;
}
Pseudocode:
Output:
Test Case 01- Normal Case:

Test Case 02:

Q2. Job sequencing Problem using branch and bound.

Approach:

The Job Sequencing Problem using Branch and Bound (B&B) aims to minimize penalties by optimally scheduling jobs
within their deadlines. The approach is:

1. Sort Jobs in decreasing order of penalty.

2. Use a Priority Queue (Min-Heap) to explore solutions, where each node represents a partial job schedule.

3. At each step, either include a job in the sequence (if a slot is available) or exclude it (adding its penalty).

4. Branch to explore both possibilities and use Bounding to prune non-optimal solutions.

5. The minimum penalty is obtained when all jobs are processed optimally.
Code:

#include <iostream>

#include <vector>
#include <queue>
#include <algorithm>
#include <climits>

using namespace std;

struct Job
{
int id;
int penalty;
int deadline;
};

struct Node
{
vector<bool> assigned;
int level;
int penalty;
};

// Comparison function for priority queue (min-heap based on penalty)


struct Compare
{
bool operator()(const Node &a, const Node &b)
{
return a.penalty > b.penalty;
}
};

int jobSequencing(vector<Job> &jobs, int maxDeadline)


{
sort(jobs.begin(), jobs.end(), [](const Job &a, const Job &b)
{ return a.penalty > b.penalty; });

priority_queue<Node, vector<Node>, Compare> pq;


Node root;
root.assigned = vector<bool>(maxDeadline, false);
root.level = 0;
root.penalty = 0;
pq.push(root);
int minPenalty = INT_MAX;

while (!pq.empty())
{
Node current = pq.top();
pq.pop();

if (current.level == jobs.size())
{
minPenalty = min(minPenalty, current.penalty);
continue;
}

int jobIndex = current.level;

// Option 1: Include the job if possible


for (int d = min(jobs[jobIndex].deadline, maxDeadline) - 1; d >= 0; --
d)
{
if (!current.assigned[d])
{
Node include = current;
include.level++;
include.assigned[d] = true;
pq.push(include);
break;
}
}

// Option 2: Exclude the job and add penalty


Node exclude = current;
exclude.level++;
exclude.penalty += jobs[jobIndex].penalty;
pq.push(exclude);
}

return minPenalty;
}

int main()
{
int N;
cout << "Enter number of jobs: ";
cin >> N;

vector<Job> jobs(N);
cout << "Enter penalty and deadline for each job: " << endl;
int maxDeadline = 0;
for (int i = 0; i < N; ++i)
{
jobs[i].id = i + 1;
cin >> jobs[i].penalty >> jobs[i].deadline;
maxDeadline = max(maxDeadline, jobs[i].deadline);
}

cout << "Min Penalty: " << jobSequencing(jobs, maxDeadline) << endl;

return 0;
}

Pseudocode:
Output:

Test Case 1:

Test Case 02:


Test case 03

End of Assignment

You might also like