Maximum Matching in Graph - algorithm

I have 2D Array where each element is O or Non Zero, I have to collect all 1 elements. For Doing so I will hire a workers
`0` - dead coconut tree
`Non Zero` - living tree
A worker starts harvesting at a living tree and continues harvesting along a straight line of trees in one of the four cardinal directions (i.e., north, south, east, or west). A worker stops harvesting coconuts when one of the following conditions is satisfied
The worker comes across a dead coconut tree.
The worker hits the edge of the plantation (i.e., there are no more
harvestable trees in that direction).
For Example Array Look Like This:
So Minimum 4 workers are required
Question:
I was shocked when i came to know this is Maximum Matching Problem , i had no idea how is it so.
Following is Code which Mark the Horizontal and Vertical Strips with a index
m=1;
for (int i=0; i<r; i++) {
int j = 0;
while (j < c && a[i][j] < m) j++;
while (j < c) {
while (j < c && a[i][j] >= m) {
h[i][j] = hcnt;
j++;
}
hcnt++;
while (j < c && a[i][j] < m) j++;
}
}
int vcnt = 0;
for (int j=0; j<c; j++) {
int i = 0;
while (i < r && a[i][j] < m) i++;
while (i < r) {
while (i < r && a[i][j] >= m) {
v[i][j] = vcnt;
i++;
}
vcnt++;
while (i < r && a[i][j] < m) i++;
}
}
So my Question is How to add edges and why it's maximum matching problem and can someone explain the intuition behind this why maximum matching works on this problem.
Original Question
Following Code Add the edges. I hade no idea how we are adding the edges and why is it working
int s = 0, t = hcnt + vcnt + 1;
for (int i=0; i<hcnt; i++) addEdge(s, i+1, 1);
for (int i=0; i<r; i++) {
for (int j=0; j<c; j++) if(a[i][j] >= m) {
addEdge(1 + h[i][j], 1 + hcnt + v[i][j], 1);
}
}
for (int i=0; i<vcnt; i++) addEdge(1 + hcnt + i, t, 1);

The idea is as follows: we need to take some vertical and horizontal lines in such a way that all trees are covered and the number of picked lines is minimized.
Each cell with a tree becomes an edge in the graph. Each vertical line becomes a vertex in the left part of the graph and each horizontal line becomes a vertex in the right part. Now the problem is equivalent to finding a vertex cover in this graph. The size of the maximum matching is equal to the size of the vertex cover in any bipartite graph (it's a more or less well-known theorem).

Related

Manhattan tourist

In my algorithms and datastructures class I have been asked to implement the Manhattan tourist problem using dynamic programming.
I have come to a solution using a combination of dynamic programming and recursive calls, but I seem to get "Time limit exceeded" when putting it to the test on CodeJudge. I haven't been able to figure out why my code isn't fast enough. Any takers?
Best regards.
Description of the problem:
Your are helping the tourist guide company "Manhattan Tourists", that are arranging
guided tours of the city. They want to find a walk between two points on the map that is both interesting and short. The map is a square grid graph. The square grid graph has n rows with n nodes in each row. Let node vi,j denote the jth node on row i. For 1≤I<n and for 1≤j≤n node vi,j is connected to vi+1, j. And for 1≤i≤n and for 1 ≤ j < n node vi,j is connected to vi,j+1. The edges have non-negative edge weights that indicate how interesting that street is. See the graph below for an example of a 5 × 5 grid graph.
They want to find a short interesting walk from the upper left corner (s = v1,1) to the lower right corner (t = vn,n). More precisely, they want to find a path with the possible smallest number of edges, and among all paths with this number of edges they want the path with the maximum weight (the weight of a path is the sum of weights on the path).
All shortest paths have 2n − 2 edges and go from s to t by walking either down or right in each step. In the example below two possible shortest paths (of length 8) are indicated. The dashed path has weigth 38 and the dotted path has weight 30.
Let W [i, j] be the maximal weight you can get when walking from s to vi, j walking either down or right in each step. Let D[i, j] be the weight of the edge going down from vi, j and let R[i, j] be the weight of the edge going right from vi,j.
Description on CodeJudge:
Exercise
Before you can solve this exercise, you must first read, understand and (partly) solve the problem Manhattan Tourists described on the weekplan.
Your task here is to implement your solution. Read the input/output specification below and look at the sample test data in order to learn how to read the input and write the output.
Input format
Line 1: The integer n (1<= n <= 1000).
Line 2..n+1: the n rows of R, each consisting of n-1 integers separated by space.
Line n+2..2n: the n-1 rows of D, each consisting of n integers separated by space.
Output format:
Line 1: The maximum interest score of a shortest walk.
Heres my code so far:
public static void main(String[] args) {
Scanner console = new Scanner(System.in);
int n = console.nextInt();
int[][] R = new int[n][n-1];
int[][] D = new int[n-1][n];
for(int i = 0; i < n; i++) {
for(int j = 0; j < n-1; j++) {
R[i][j] = console.nextInt();
}
}
for(int i = 0; i < n-1; i++) {
for(int j = 0; j < n; j++) {
D[i][j] = console.nextInt();
}
}
System.out.println(opt(R, D, n, n-1, n-1));
}
public static int opt(int[][]R, int[][]D, int n, int i, int j) {
int[][] result = new int[n][n];
if(i==0 && j==0) {
if(result[i][j] == 0) {
result[i][j] = 0;
}
return result[i][j];
} else if(i == 0) {
if(result[i][j] == 0) {
result[i][j] = opt(R,D,n,i,j-1) + R[i][j-1];
}
return result[i][j];
}else if(j == 0) {
if(result[i][j] == 0) {
result[i][j] = opt(R,D,n,i-1,j) + D[i-1][j];
}
return result[i][j];
}else if(result[i][j] == 0) {
result[i][j] = max(opt(R, D, n, i, j-1) + R[i][j-1],opt(R, D, n, i-1, j) + D[i-1][j]);
}
return result[i][j];
}
public static int max(int i, int j) {
if(i > j) {
return i;
}
return j;
}
}
Why a recursion?
The topmost row can be traversed horizontally only. So, for each vertex in the first row the total weight is a sum of weights of branches to the left. You can compute all of them in a single loop as a running total across the row.
For each next row the total weight of the first vertex is a weight of the vertex above it plus the weight of the branch between them. And the total weight of each next vertex in the row is the bigger one from two possible when coming from above or from left.
All that can be computed iteratively with two nested loops.

Construct any bipartite graph with degree constraints

We need to construct a bipartite graph with N vertices each, on the two parts, and with total number of edges equal to M.
The vertices on the left are numbered from 1 to N.
The vertices on the right are also numbered from 1 to N.
The degree of every vertex to be greater than or equal to X, and to be lesser than or equal to Y. i.e. for all v, X ≤ deg(v) ≤ Y
Given four integers N, M, X, Y we need to construct some bipartite graph satisfying this property. If there does not exist any such graph, then also tell the same.
Example :
If N=2 , M=3 , X=1 and Y=2
then the 3 edges in bipartite graph will be : (1,1),(2,2) and (1,2)
If N=2 , M=3 , X=1 and Y=1 then no bipartite graph exists.
How can this problem be solved if
1 ≤ N ≤ 100
1 ≤ X ≤ Y ≤ N
0 ≤ M ≤ N * N
Original question link
Obviously, the variables need to satisfy:
X * N <= M <= Y * N
Otherwise, there will be no solution.
Finding the edges could be done in waves. Start by connecting each node i from the first set to the according node i from the second set. In the next wave, connect i with (i + 1) mod N. Then i with (i + 2) mod N and so one. This will increase the degree of each vertex by exactly one in each wave. Stop whenever you have constructed M edges. This may also happen during a wave.
ACM ICPC 2016 India Preliminary Round Problem.
Link
The contest is now ended. I couldn't submit the answer (was about to submit the code just 10 secs before the end and my Internet stopped working).
d is equivalent to X in the OP's version of the problem.
D is equivalent to Y in the OP's version of the problem.
t is the number of test cases.
I made the code as per the original question in the link.
The logic is similar to
Nico Schertler's one. My complexity will be a little more because instead of just connecting, i+xth node to i in the xth iteration, I have used a set that finds the first element not connected in the range [1..N] and connects them.
This is my code:
#include <bits/stdc++.h>
using namespace std;
int main() {
int t, n, m, d, D;
cin >> t;
while(t--) {
cin >> n >> m >> d >> D;
if(n*D < m || n*d > m)
printf("-1\n");
else {
vector <set <int> > v(n);
int edges = 0, count = 0;
while(count != d) {
for(int i = 0; i < n; i++) {
for(int j = 0; j < n; j++) {
if(v[i].find(j) == v[i].end()) {
v[i].insert(j);
++edges;
break;
}
if(edges == m)
break;
}
if(edges == m)
break;
}
++count;
}
while(edges < m) {
for(int i = 0; i < n; i++) {
if(v[i].size() == D)
continue;
for(int j = 0; j < n; j++) {
if(v[i].find(j) == v[i].end()) {
v[i].insert(j);
++edges;
break;
}
if(edges == m)
break;
}
if(edges == m)
break;
}
}
for(int i = 0; i < n; i++) {
set <int>::iterator it = v[i].begin();
for(; it != v[i].end(); ++it) {
printf("%d %d\n", i+1, (*it)+1);
}
}
}
}
return 0;
}
I don't know whether this code is correct or not.

Sort a given array whose elements range from 1 to n , in which one element is missing and one is repeated

I have to sort this array in O(n) time and O(1) space.
I know how to sort an array in O(n) but that doesn't work with missing and repeated numbers. If I find the repeated and missing numbers first (It can be done in O(n)) and then sort , that seems costly.
static void sort(int[] arr)
{
for(int i=0;i<arr.length;i++)
{
if(i>=arr.length)
break;
if(arr[i]-1 == i)
continue;
else
{
while(arr[i]-1 != i)
{
int temp = arr[arr[i]-1];
arr[arr[i]-1] = arr[i];
arr[i] = temp;
}
}
}
}
First, you need to find missing and repeated numbers. You do this by solving following system of equations:
Left sums are computed simultaneously by making one pass over array. Right sums are even simpler -- you may use formulas for arithmetic progression to avoid looping. So, now you have system of two equations with two unknowns: missing number m and repeated number r. Solve it.
Next, you "sort" array by filling it with numbers 1 to n left to right, omitting m and duplicating r. Thus, overall algorithm requires only two passes over array.
void sort() {
for (int i = 1; i <= N; ++i) {
while (a[i] != a[a[i]]) {
std::swap(a[i], a[a[i]]);
}
}
for (int i = 1; i <= N; ++i) {
if (a[i] == i) continue;
for (int j = a[i] - 1; j >= i; --j) a[j] = j + 1;
for (int j = a[i] + 1; j <= i; ++j) a[j] = j - 1;
break;
}
}
Explanation:
Let's denote m the missing number and d the duplicated number
Please note in the while loop, the break condition is a[i] != a[a[i]] which covers both a[i] == i and a[i] is a duplicate.
After the first for, every non-duplicate number i is encountered 1-2 time and moved into the i-th position of the array at most 1 time.
The first-found number d is moved to d-th position, at most 1 time
The second d is moved around at most N-1 times and ends up in m-th position because every other i-th slot is occupied by number i
The second outer for locate the first i where a[i] != i. The only i satisfies that is i = m
The 2 inner fors handle 2 cases where m < d and m > d respectively
Full implementation at http://ideone.com/VDuLka
After
int temp = arr[arr[i]-1];
add a check for duplicate in the loop:
if((temp-1) == i){ // found duplicate
...
} else {
arr[arr[i]-1] = arr[i];
arr[i] = temp;
}
See if you can figure out the rest of the code.

Max sum in an array with constraints

I have this problem , where given an array of positive numbers i have to find the maximum sum of elements such that no two adjacent elements are picked. The maximum has to be less than a certain given K. I tried thinking on the lines of the similar problem without the k , but i have failed so far.I have the following dp-ish soln for the latter problem
int sum1,sum2 = 0;
int sum = sum1 = a[0];
for(int i=1; i<n; i++)
{
sum = max(sum2 + a[i], sum1);
sum2 = sum1;
sum1 = sum;
}
Could someone give me tips on how to proceed with my present problem??
The best I can think of off the top of my head is an O(n*K) dp:
int sums[n][K+1] = {{0}};
int i, j;
for(j = a[0]; j <= K; ++j) {
sums[0][j] = a[0];
}
if (a[1] > a[0]) {
for(j = a[0]; j < a[1]; ++j) {
sums[1][j] = a[0];
}
for(j = a[1]; j <= K; ++j) {
sums[1][j] = a[1];
}
} else {
for(j = a[1]; j < a[0]; ++j) {
sums[1][j] = a[1];
}
for(j = a[0]; j <= K; ++j) {
sums[1][j] = a[0];
}
}
for(i = 2; i < n; ++i) {
for(j = 0; j <= K && j < a[i]; ++j) {
sums[i][j] = max(sums[i-1][j],sums[i-2][j]);
}
for(j = a[i]; j <= K; ++j) {
sums[i][j] = max(sums[i-1][j],a[i] + sums[i-2][j-a[i]]);
}
}
sums[i][j] contains the maximal sum of non-adjacent elements of a[0..i] not exceeding j. The solution is then sums[n-1][K] at the end.
Make a copy (A2) of the original array (A1).
Find largest value in array (A2).
Extract all values before the it's preceeding neighbour and the values after it's next neighbour into a new array (A3).
Find largest value in the new array (A3).
Check if sum is larger that k. If sum passes the check you are done.
If not you will need to go back to the copied array (A2), remove the second larges value (found in step 3) and start over with step 3.
Once there are no combinations of numbers that can be used with the largest number (i.e. number found in step 1 + any other number in array is larger than k) you remove it from the original array (A1) and start over with step 0.
If for some reason there are no valid combinations (e.g. array is only three numbers or no combination of numbers are lower than k) then throw an exception or you return null if that seems more appropriate.
First idea: Brute force
Iterate all legal combination of indexes and build the sum on the fly.
Stop with one sequence when you get over K.
keep the sequence until you find a larger one, that is still smaller then K
Second idea: maybe one can force this into a divide and conquer thing ...
Here is a solution to the problem without the "k" constraint which you set out to do as the first step: https://stackoverflow.com/a/13022021/1110808
The above solution can in my view be easily extended to have the k constraint by simply amending the if condition in the following for loop to include the constraint: possibleMax < k
// Subproblem solutions, DP
for (int i = start; i <= end; i++) {
int possibleMaxSub1 = maxSum(a, i + 2, end);
int possibleMaxSub2 = maxSum(a, start, i - 2);
int possibleMax = possibleMaxSub1 + possibleMaxSub2 + a[i];
/*
if (possibleMax > maxSum) {
maxSum = possibleMax;
}
*/
if (possibleMax > maxSum && possibleMax < k) {
maxSum = possibleMax;
}
}
As posted in the original link, this approach can be improved by adding memorization so that solutions to repeating sub problems are not recomputed. Or can be improved by using a bottom up dynamic programming approach (current approach is a recursive top down approach)
You can refer to a bottom up approach here: https://stackoverflow.com/a/4487594/1110808

Are these 2 knapsack algorithms the same? (Do they always output the same thing)

In my code, assuming C is the capacity, N is the amount of items, w[j] is the weight of item j, and v[j] is the value of item j, does it do the same thing as the 0-1 knapsack algorithm? I've been trying my code on some data sets, and it seems to be the case. The reason I'm wondering this is because the 0-1 knapsack algorithm we've been taught is 2-dimensional, whereas this is 1-dimensional:
for (int j = 0; j < N; j++) {
if (C-w[j] < 0) continue;
for (int i = C-w[j]; i >= 0; --i) { //loop backwards to prevent double counting
dp[i + w[j]] = max(dp[i + w[j]], dp[i] + v[j]); //looping fwd is for the unbounded problem
}
}
printf( "max value without double counting (loop backwards) %d\n", dp[C]);
Here is my implementation of the 0-1 knapsack algorithm: (with the same variables)
for (int i = 0; i < N; i++) {
for (int j = 0; j <= C; j++) {
if (j - w[i] < 0) dp2[i][j] = i==0?0:dp2[i-1][j];
else dp2[i][j] = max(i==0?0:dp2[i-1][j], dp2[i-1][j-w[i]] + v[i]);
}
}
printf("0-1 knapsack: %d\n", dp2[N-1][C]);
Yes, your algorithm gets you the same result. This enhancement to the classic 0-1 Knapsack is reasonably popular: Wikipedia explains it as follows:
Additionally, if we use only a 1-dimensional array m[w] to store the current optimal values and pass over this array i + 1 times, rewriting from m[W] to m[1] every time, we get the same result for only O(W) space.
Note that they specifically mention your backward loop.

Resources