Showing posts with label SPOJ. Show all posts
Showing posts with label SPOJ. Show all posts

Monday, February 20, 2012

Silly Sort (Spoj - 2277, UVa - 1016)

Today I'll discuss the problem Silly Sort from spoj, which is also in uva.

In this problem you have to sort an array of integers in ascending order. Quite familiar to you. But here you are only allowed to swap any two elements. And each swap has a cost which is the sum of those two elements. You have to sort the array in minimum cost.

Seems tricky, so let's brainstorm. At first we have to know which element will go to which place after the complete sort. We can easily calculate that by copying the array into another array and sorting that array. Let's say original array is a and sorted array is b. So b[i] will go to ith place for each i, in other word i is the destination of b[i]. First thing comes to our mind is, in each swap we will ensure that at least one element will move to it's destination. This will ensure minimum number of swaps, but will it confirm the minimum cost?

Let's work with the first example -
3
3 2 1
Sorted array will be -
1 2 3
So If we want to ensure at least one element will go to it's destination in each swap, then we have to swap 1 and 3, and after that that is our sorted array, cost is 4, which is minimum cost. So it's working for this example.

Let's try with another example
3
3 1 2
If we swap 3 and 1, then 1 will go to it's destination and the array will become - {1, 3, 2}, now we have to swap 3 and 2, then it will be sorted. Cost is (3 + 1) + (3 + 2) = 9. But in first move if we swap 1 and 2, it will become {3, 2, 1} and then swapping 3 and 1 will make it sorted and the cost will be (1 + 2) + (3 + 1) = 7. So two different ordering causes two different costs. So what will be our strategy?

Let's work with 4th example given with the problem -
6 
8 4 5 3 2 7 
Sorted array will be -
2 3 4 5 7 8

So, 2 will go to 8's place, 3 will go to 4's place, etc. Let's draw these relations.


In the above image, it is seen that 2 will go to 8's place, 8 will go to 7's place, 7 will go to 2's place. These three formed a cycle, another cycle is 3 → 4 → 5 → 3. So every input can be decomposed into some cycles.

So, our next approach is solving each cycles independently and sum of the costs for all cycles is our result. But what is the cost of solving a cycle. We see If we swap 2 and 8 of 2 → 8 → 7 → 2 cycle. 2 will go to it's destination, and 7 → 8 → 7 will form a smaller cycle.

If we swap two elements in a cycle of length K > 1 such that at least 1 element of this cycle will go to it's destination. Then after the swap, it will be splited into two cycles, one with length K - 1 and another one with length 1. We don't need to do anything with the cycle of length 1 because the element is already in it's destination.

Now for solving a cycle optimally, we need some observations -
1. Every element of this cycle will participate in at least 1 swap.
2. We need K - 1 swaps to sort this cycle. Each swap will add two elements to the cost. So 2 × (K - 1) elements will be added to the cost. There are K elements. So some elements will be added multiple times.

So if we want to move an element x to it's destination by swapping with another value, we should try to swap with the minimum value of that cycle. If we can do that for every element other than the smallest element. Than in the final cost, every element will be added once and the minimum element will be added K - 1 times. And we cannot make any less cost by confirming (1) and (2).  So this is the optimal approach to solve a cycle.

So In the above cycle 2 → 8 → 7 → 2, we'll swap 7 and 2 first. Cost is 9, then 8 and 2, cost is 10. So total cost is 19 for this cycle. In the same way, solving the second cycle 3 → 4 → 5 → 3 will cost 15. Total cost for this case is 34. This is the result of that example also.

Now let's try example 3 -
5 
1 8 9 7 6 
There are two cycles.
1 → 1
6 → 8 → 7 → 9 → 6

Solving the first cycle costs 0, and second cycle costs 42, total 42. But result is 41. So solving every cycles independently will not give the best result. Here in this example, the minimum element is 6 in the second cycle. As the cycle length is 4 it will be added 3 times which will cost 18. But if know there is an 1 in another cycle. We can borrow it by swapping with 6, which will cost 6 + 1 = 7, then do the sorting this time we use 1 instead of 6, this will add 3 instead of 18. After sorting 1 will be in place of 6's destination and we have to give the 1 back to it's own cycle and bring our 6 back. We can do it by one more swap which will cost another 7. So total cost 7 + 3 + 7 = 17 which is 1 less than 18. That's how the result is 41. So we'll try to borrow the minimum value of the whole given array and see whether it gives the optimal results or not.

Here is my simple solution in c++.

#include <iostream>
#include <cstdio>
#include <map>
#include <algorithm>

using namespace std;

int a[100000];
int b[100000];
bool vis[100000];

int main (void) {
    int N;
    int t = 0;
    while (scanf("%d", &N) && N) {
        t++;
        for (int i = 0; i < N; ++i) {
            scanf("%d", &a[i]);
            b[i] = a[i];
            vis[i] = false;
        }
        sort(b, b + N);

        // Map the numbers to their desired place after sort
        map<int, int> place;
        
        for (int i = 0; i < N; ++i) {
            place[b[i]] = i;
        }
    
        int res = 0;
        for (int i = 0; i < N; ++i) {
            if (vis[i] == false) {
                if (place[a[i]] == i) {
                    vis[i] = true;
                    continue;
                }
                // We're in new cycle
                int min_val = a[i];
                int num = 0;
                int sum = 0;
                int j = i;

                while (vis[j] == false) {
                    sum += a[j];
                    num++;
                    if (a[j] < min_val) {
                        min_val = a[j];
                    }
                    vis[j] = true;
                    j = place[a[j]];
                }
                sum -= min_val;
                res += sum + min_val * (num - 1);
                
                // Let's try to borrow the minimum value.
                // If it's less costly then update our result.
                if (2 * (b[0] + min_val) <
                    (min_val - b[0]) * (num - 1)) {
                    res -= (min_val - b[0]) * (num - 1) -
                           2 * (b[0] + min_val);
                }    
            }
        }
        printf("Case %d: %d\n\n", t, res);
    }
    return 0;
}

Saturday, August 20, 2011

Maximum Triangle Area (Part 3 and final)

Part 1
Part 2

After second part, our algorithm became something like this -
for each 0 <= i < n, initialize A = Pi, B = P(i + 1) % n, C = P(i + 2) % n, then move C counter clockwise until area non-decreasing, move B to its adjacent point counter clockwise, if area non-decreasing, move C again until area non decreasing and so on. You will get the maximum area for each A and output the maximum of these areas. For a given point starting point Pi, if the maximum area is ABC. We can say ABC is 'stable' triangle for A, in other word s(pi, Pi + 1, Pi + 2) = ABC, that means we cannot move B or C any further by not decreasing the area.

Now, after getting a maximum ABC for a fixed first point Pi, we will start with Pi + 1, Pi + 2 and Pi + 3. We get A'B'C' as a stable triangle i.e. s(pi, pi + 1, pi + 2) = A'B'C'. Hence our algorithm is O(n ^ 2). But it can be proved that, rather than, start from Pi + 1 as a second point and Pi + 2 as a third point, we can start with B and C as a second and third point and get the same result, i.e. s(Pi + 1, Pi + 2, Pi + 3) = s(Pi + 1, B, C), where s(Pi, Pi + 1, Pi + 2) = ABC.

Lets assume that we are wrong, and s(Pi + 1, Pi + 2, Pi + 3) = A'B'C' > s(pi, Pi + 1, Pi + 2).

Case 1:
B' < B and C' = C


We know that,
ABC >= AB'C (otherwise largest triangle for A would be AB'C)
=> ABC + BB'A >= AB'C + BB'A
=> AB'BC >= AB'C + BB'A
=> AB'C + BB'C >= AB'C + BB'A
=> BB'C >= BB'A
=> BB'C >= BB'A' (for base BB' area is non decreasing from BB'C to BB'A, so it
will be non decreasing from BB'A to BB'C)
=> BB'C + A'B'C >= BB'A' + A'B'C
=> A'B'BC >= BB'A' + A'B'C
=> A'BC + BB'A' >= BB'A' + A'B'C
=> A'BC >= A'B'C
=> A'BC >= A'B'C' ...... [1] (as C = C')

So, for case 1, s(Pi + 1, Pi + 2, Pi + 3) = s(Pi + 1, B, C).

Case 2:
Let's say, A'B'C' is the largest triangle for first point A'.
Where, B' < B and C' > C. See the following image.



For first point A, ABC is the largest trianlge,
So, ABC >= AB'C
=> ABC + BB'C >= ABC + BB'C
=> ABC + BB'C >= AB'BC
=> ABC + BB'C >= ABC + BB'A
=> BB'C >= BB'A
i.e. BB'A <= BB'C Hence, BB'A <= BB'C' (otherwise, BB'A > BB'C and BB'A <= BB'C, which is not possible for a convex polygon) So, BB'A' <= BB'C' ... [2] (as the triangle area non increasing by moving C' to A, it will be non increasing from A to A') Now as we are considering, for first point A', the largest area triangle is A'B'C'. So, A'B'C' > A'BC'
=> A'B'C' + BB'A' > A'BC' + BB'A
=> A'B'C' + BB'A' > A'B'BC'
=> A'B'C' + BB'A' > A'B'C' + BB'C'
=> BB'A' > BB'C', this contradicts to [2].

So it is not possible to have, A'B'C' > A'BC' i.e. for case 2, s(Pi + 1, Pi + 2, Pi + 3) = s(Pi + 1, B, C).

Case 3:
Let's say, A'B'C' is the largest triangle for fixed first point A', where,
B' < B and C' < C

As ABC is the largest triangle having first point A,
either ABC' >= AB'C' (by moving B' to B, area increases) .. (3A)
or AB'C >= AB'C' (by moving C' to C, area increases) .. (3B)

Case 3A
ABC' >= AB'C'
=> ABC' + BB'A >= AB'C' + BB'A
=> AB'BC' >= AB'C' + BB'A
=> AB'C' + BB'C' >= AB'C' + BB'A
=> BB'C' >= BB'A
=> BB'C' >= BB'A' (As by moving C' to A area non increasing, moving A to A' will area be non increasing)
=> BB'C + A'B'C' >= BB'A' + A'B'C'
=> BB'A'C' >= BB'A' + A'B'C'
=> BB'A' + A'BC' >= BB'A' + A'B'C'
=> A'BC >= A'B'C'

So it is not possible to have, A'B'C' > A'BC' i.e. for case 3A, s(Pi + 1, Pi + 2, Pi + 3) = s(Pi + 1, B, C).

Case 3B
AB'C >= AB'C'
=> AB'C + CC'B' >= AB'C' + CC'B'
=> AB'C'C >= AB'C' + CC'B'
=> AB'C' + CC'A >= AB'C' + CC'B'
=> CC'A >= CC'B'
=> CC'A' >= CC'B' (As moving A to B', area decreases, it's not possible moving A' to B' area increases)
=> CC'A' + A'B'C' >= CC'B' + A'B'C'
=> A'B'C'C >= CC'B' + A'B'C'
=> CC'B' + A'B'C >= CC'B' + A'B'C'
=> A'B'C >= A'B'C'

So it is not possible to have, A'B'C' > A'B'C i.e. for case 3B, s(Pi + 1, Pi + 2, Pi + 3) = s(Pi + 1, B, C).

Case 4:
C' < C and B' = B

Here,
ABC >= ABC'
=> ABC + CC'A >= ABC' + CC'A
=> ABC + CC'A >= ABC'C
=> ABC + CC'A >= ABC + CC'B
=> CC'A >= CC'B
=> CC'A' >= CC'B
=> CC'A' + A'BC' >= CC'B + A'BC'
=> A'BC'C >= CC'B + A'BC'
=> A'BC + CC'B >= CC'B + A'BC'
=> A'BC >= A'BC'
=> A'BC >= A'B'C' (as B' = B)

So it is not possible to have, A'B'C' > A'BC i.e. for case 3B, s(Pi + 1, Pi + 2, Pi + 3) = s(Pi + 1, B, C).

Case 5:
B' > B and C' < C


Now,
A'BC >= A'BC' (From case 4)
=> A'BC + CC'B >= A'BC' + CC'B
=> A'BC'C >= A'BC' + CC'B
=> A'BC' + CC'A' >= A'BC' + CC'B
=> CC'A' >= CC'B
=> CC'A' >= CC'B'
=> CC'A' + A'B'C' >= CC'B' + A'B'C'
=> CC'B'A' >= CC'B' + A'B'C'
=> CC'B' + A'B'C >= CC'B' + A'B'C'
=> A'B'C >= A'B'C'

So it is not possible to have, A'B'C' > A'B'C i.e. for case 5, s(Pi + 1, Pi + 2, Pi + 3) = s(Pi + 1, B, C).

So, from all of the 5 possible cases, we found that, s(Pi + 1, Pi + 2, Pi + 3) = s(Pi + 1, B, C). So our algorithm will still work, if we start our iteration for first point i = i + 1, without changing the position 2nd and 3rd point. We will move first point from 0 to n - 1, and 2nd and 3rd point will also not move more than n times. So our algorithm in worst case is O (3 * n) = O(n)

My simple implementation in C++.


#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<vector>
#include<string>
#include<algorithm>
#include<set>
#include<map>
#include<cassert>
#include<sstream>
#include<queue>
#include<stack>
#include<iostream>
using namespace std;

struct Point {
int x;
int y;
Point(int _x, int _y) : x(_x), y(_y) {}
Point() : x(0), y(0) {}
bool operator <(const Point& p) const {
return x < p.x || (x == p.x && y < p.y);
}
};

bool left_turn(const Point& p1, const Point& p2, const Point& p3) {
return (p2.y - p1.y) * (p3.x - p1.x) - (p2.x - p1.x) * (p3.y - p1.y) > 0;
}

// Returns list of points of convex hull in counter clockwise
// The last point and first point will be equal
vector<Point> convex_hull(vector<Point> p) {
vector<Point> ret;
sort(p.begin(), p.end());
// build lower hull
for (int i = 0; i < p.size(); ++i) {
while (ret.size() >= 2 &&
left_turn(ret[ret.size() - 2], ret[ret.size() - 1], p[i])) {
ret.pop_back();
}
ret.push_back(p[i]);
}
int lower_hull_size = ret.size();
// build upper hull
for (int i = p.size() - 2; i >= 0; --i) {
while (ret.size() - lower_hull_size >= 1 &&
left_turn(ret[ret.size() - 2], ret[ret.size() - 1], p[i])) {
ret.pop_back();
}
ret.push_back(p[i]);
}
return ret;
}

double triangle_area (const Point& p1, const Point& p2, const Point& p3) {
return abs((double) p1.x * p2.y +
(double) p2.x * p3.y +
(double) p3.x * p1.y -
(double) p1.y * p2.x -
(double) p2.y * p3.x -
(double) p3.y * p1.x) / 2.0;
}

int main (void)
{
int n;
while (scanf("%d", &n) && n != -1) {
vector<Point> p;
for (int i = 0; i < n; ++i) {
int x, y;
scanf("%d%d", &x, &y);
p.push_back(Point(x, y));
}
p = convex_hull(p); p.pop_back();
int i = 0;
int j = i + 1;
int k = j + 1;
double res = 0.;
while (true) {
double area = triangle_area(p[i], p[j], p[k]);
while (true) {
while (true) {
int nk = (k + 1) % n;
double narea = triangle_area(p[i], p[j], p[nk]);
if (narea >= area) {
k = nk;
area = narea;
} else {
break;
}
}
int nj = (j + 1) % n;
double narea = triangle_area(p[i], p[nj], p[k]);
if (narea >= area) {
j = nj;
area = narea;
} else {
break;
}
}
if (area > res) res = area;
i++;
if (i == j) j = (j + 1) % n;
if (j == k) k = (k + 1) % n;
if (i == n) break;
}
printf("%.2lf\n", res);
}
return 0;
}



Thursday, August 18, 2011

Maximum Triangle Area (Part 2)

I hope you enjoyed the part 1. In part - 1. We came up to an algorithm of complexity O(n ^ 2 log n). We'll try to improve it now.

Our problem is now reduced to: Given a convex polygon having n points. Find three points from these n points which form the largest triangle area.

Let's consider our points are ordered in counter clockwise and named P0, P1, ... , Pn - 1.

Let's start with A = P0, B = P1, C = P2. Now we will be moving C counter clockwise order (i.e. P3, P4, ...), as long as the triangle area doesn't decrease. And we know(from part 1), the area will not increase anymore after this point. So let's stop moving C. Let's say, we stopped C at Pk. So we found the largest triangle, having A = P0, and B = P1. Now it's time to move B. Let's move B to the adjacent point clockwise, if the area doesn't decreases. Now for new AB we have to find the best position of C, so that area is maximized. We will find that, we don't need to iterate from the next point of B, rather we can proceed with C as where it is now and try to move it counter clockwise as long the area doesn't decrease. Will it work always? Let's consider that it will not work, i.e. There will be a case where for a given AB, we will find maximum triangle ABC, and after moving B to B', we will find maximum triangle AB'C', where C' comes before C (By C' comes before C, I mean if we count points of the polygon starting from A, C' will come before C), and AB'C' > ABC, as the following picture.




In the image above, we got ABC as the largest triangle for base AB. And after moving B to B', we got the largest triangle AB'C', where C' comes before C.
we know that, ABC >= ABC' ... [1] (As ABC is the largest triangle for base AB)
Now,
ABC' + ACC' = ABC'C = ABC + BCC'
=> ACC' >= BCC' [2] (as ABC >= ABC' from [1])

From (2), we can say, ACC' >= B'CC' (We already told for a given base, AB, if we move another point C, it will increase area of ABC up to a certain point and than start decreasing, i.e. it will never increase again after that point. Here for base CC', it started decrease from point A to B, so it will not increase from B to B'. As ACC' >= BCC', so ACC' >= B'CC').

ACC' >= B'CC'
=> ACC' + AB'C' >= B'CC' + AB'C' (adding AB'C' in both side)
=> AB'C'C >= B'CC' + AB'C'
=> AB'C + B'CC' >= B'CC' + AB'C'
=> AB'C >= AB'C'

So, for base AB' any point C' before C will not increase the area. So we can safely iterate from current position of C to find a larger area triangle. After finding the best position of C, move B again, if it increases the area. If moving B doesn't increases the area, stop moving B. Question is, is there any chance of getting larger triangle don't stop moving B in this case? Let's consider that there is a chance to get a larger area.



For example, in the above picture, for base AB we found ABC as the largest triangle, but after moving B to B' we found AB'C < ABC. Now for base AC, we already know that there area will not increase any more by moving B. And we also proved that area will not increase moving C in clockwise. So only way left is moving C counter clockwise. Let's say we found a solution AB'C', where C' comes after C. As this is a better solution, AB'C' > AB'C

Now, ABC > ABC' (If ABC' >= ABC, we would have already moved C to C' for base AB).
=> ABC + CC'A > ABC' + CC'A
=> ABCC' > ABC' + CC'A
=> ABC' + CC'B > ABC' + CC'A
=> CC'B > CC'A [3]

And, AB'C' > AB'C
=> AB'C' + CC'B' > AB'C + CC'B
=> AB'CC' > AB'C + CC'B
=> AB'C + CC'A > AB'C + CC'B
=> CC'A > CC'B [4]

[3] and [4] are contradictory. So if after moving B to B', the area decreases, it will not possible to get larger area for the same A. So we have to stop moving B, and this ABC is the largest triangle for a given A. As B can move up to n times and also C can move up to n times. So for a given A, our algorithm complexity is O(2n) ~ O(n). We have to do this for each possible position of A. So our algorithm becomes O(n ^ 2).

Hopefully you enjoyed it so far. We'll try to improve more in part 3.



Wednesday, August 17, 2011

Maximum Triangle Area (Part 1)

Today I'll discuss about another interesting problem. Though the analysis is big, hopefully you'll find it interesting.

Problem name: Maximum Triangle Area
Problem description:
Given n distinct points on a plane, your task is to find the triangle that have the maximum area, whose vertices are from the given points.

The most obvious solution is trying all possible triangle and find out the maximum area. The complexity of that algorithm is O(n^3). Which is huge as n can be as large as 50000.

So what can we do now? An interesting fact is, we can solve this problem by only considering the points of the convex hull for the given set of points. This will work because, if there is a triangle which has a vertex A which is not in the boundary of the convex hull, there will be always a point X in convex hull such that, by moving A to X we can increase the area of the triangle. Let's see an example -

We have a set of points {A, B, C, D, E, F, G, H, I}. The convex hull of these points consists of {D, E, F, G, H, I}. But let's claim that the largest triangle using these points is ABC. None of it's vertex is in convex hull. Let's say, BC is the base of this triangle and AP (perpendicular distance from A to BC) is the height of the triangle. So, the area of the triangle is 0.5 * BC * AP. Now draw a line parallel to the BC which goes through A. As A is inside the convex hull, you will find at least one vertex from the convex hull which is on the other side of the parallel line. Here in our example, we found D and BC are in the opposite side of the parallel line, so perpendicular distance from D to BC (DQ) is greater than AP. Hence, 0.5 * BC * AB < 0.5 * BC * DQ. So, triangle area DBC > triangle area ABC. See the following picture to make it clear -


Now, we got a triangle DBC which has larger area than ABC, but yet we have two vertices in it which are not in the convex hull. Now in the similar way considering DB as a base, and drawing a parallel line of DB which goes through C, we will find that area of DBF is greater than area of DBC. In the next step we can easily show again, that area of DBH is greater than the area of DBF. So we found that, maximum area triangle can be found using only convex hull vertices.



But if all of the given points is in the convex hull, our algorithm is still O(n ^ 3). Using the convex hull property we can improve it. Now our problem is finding the largest triangle using the vertices of a convex polygon. Let's say we have n vertices 0 to n - 1. We will try to find out three vertices A, B, C in clockwise direction from these vertices which will form the maximum triangle.

Now for a given AB, rather than iterating all of the points between B and A, to find the C, we can use ternary search to find C, as if we consider AB as a base, and iterate from the next point of B one by one, the height (of the triangle ABC) will increase in every step up to a certain vertex, and after that height will decrease in every step. This way, we can ternary search the point C for every possible AB, and the complexity will become (n ^ 2 log n), which is still not enough.

As, the analysis becoming large and large, we'll discuss the other improvements in the next part.

(Special thanks to Anna Fariha for helping me understanding some proofs of this analysis.)