CHEFSQUA-Editorial

PROBLEM LINK:

Practice
Contest

Author: Dmytro Berezin
Tester: Jingbo Shang
Editorialist: Ajay K. Verma

DIFFICULTY:

Easy

PREREQUISITES:

ad-hoc, basic geometry

PROBLEM:

Given a set of lattice points, find the minimum number of points that should be added to this set such that there exist at least one square with vertices on the points of the extended set.

EXPLANATION:

Even though it is not mentioned in the problem statement that all points of the set are distinct, and we are only interested in non-degenerate square (square with non-zero size), the test cases had only distinct set of points. Nevertheless, allowing degenerate square does not make the problem any harder. We can compute the minimum number of addendum points to create a degenerate and non-degenerate square, and then take the minimum of the two.

Degenerate Squares:

A degenerate square will have all four vertices at the same location. Hence, we just need to find the frequency of the point that occurs most number of times in the set. If the frequency x is >= 4, then we already have a degenerate square and the answer should be zero, otherwise we need to add (4 - x) copies of this point to get a degenerate square.

Non-Degenerate Squares:

Now, we can assume that all points in the set are distinct. We can first consider some basic cases:

  1. If the set is empty, then we need to add 4 points.
  2. If the set has only one point, then we need to add 3 points to make a square
  3. If the set has only two points then we can add two more points to make a square.

The only interesting case is when we have 3 or more points, in which case the answer should be 0, 1, or 2. We only need to check, if there is already an square in the set (in which case the answer should be zero), or if there are three vertices of a square present in the set (in which case the answer should be 1). If none of the two cases hold, then the answer must be 2, as we can always pick two points from the set and add two more points to make a square (explained in the next section).

Extend Square with Two Points:

Suppose we have two points A (x1, y1) and B (x2, y2), and we want to create a square whose edge is the line segment joining the two points. Let us call this square ABCD, so we need to find the co-ordinates of C and D.

In a complex plane, the ray segment AB can be represented as
AB = (x2 - x1) + i (y2 - y1)

Since the length of AB and AD is the same and the angle between them is 90 degrees, the ray segment AD can be obtained by rotating AB to 90 degrees. In other words
AD = AB * (cos 90 + i sin 90)
AD = -(y2 - y1) + i (x2 - x1)

which can be used to compute the co-ordinate of D, i.e.,
D = (x1 - (y2 - y1), y1 + (x2 - x1))

Also note that the ray segment AD and BC are the same in the complex plane, hence we can use the same to compute the co-ordinates of C, i.e.,
C = (x2 - (y2 - y1), y2 + (x2 - x1))

Note that, in the above explanation we rotated AB to clockwise by 90 degrees. On the other hand, if we rotate AB anti-clockwise by 90 degrees, we will find a different square.

Check if Three/Four Vertices of Square Exist in the Set:

If there are three or more vertices of the square are present in the set, then two of these points would be the consecutive vertices of the underlying square. Hence, we can iterate through all pair of points in the set, create a square with the two points making an edge of the square, and check how many of the remaining vertices (which we can compute as explained in the previous section) of the square exist in the set. If for any pair, we have both remaining points in the set, then we already have a square. If for any pair, we have one of the remaining points in the set, then we have three vertices of the square.

Note that, one must consider both squares that can be formed by considering the line segment joining the two points as an edge.

In order to test the membership of a point in the set, one could use a set (which has O (lg N) query time), or a hash table with a properly chosen hash function (which will have O (1) amortized query time).

Time Complexity:

O (N2)

AUTHOR’S AND TESTER’S SOLUTIONS:

Author’s solution will be put up soon.
Tester’s solution will be put up soon.

15 Likes

I first saw this problem, it was not that hard but the thought of brute-forcing at (N^4) would give tle. It then hit me that iterations were O(N^2).

Initially I was implementing a hashmap using vectors : vector<vector > points( num ,vector( num , 0)); SIG-ABRT solution where num is size.

But then I thought of putting points as pair<int,int> with O(logN ) retrieval using either MAP or SET. I used set as it was easier to implement in this case.
And overall complexity was O(N^2 logN), after runtime errors and countless wa ( All tries ) I succeeded :slight_smile:

CodeChef: Practical coding for everyone ACed solution

3 Likes

I considered each pair of points to be the end points of a diagonal and then tried to find out the other two points. Sourced from [here][1]. Can any one explain how my approach was different from the above given approach?
I got AC for my


[2]


  [1]: http://math.stackexchange.com/questions/506785/given-two-diagonally-opposite-points-on-a-square-how-to-calculate-the-other-two
  [2]: http://www.codechef.com/viewsolution/5050170
1 Like

Can someone please tell the mistake with this one. The code is giving TLE. I am using a map which maps pair of coordinates to a boolean value which checks for the existence.

Also, the problem can be solved this manner: coordinates can be generated using distance formula as the sides are equal and since the sides are perpendicular we can find the points using slope i.e. product slopes of any two perpendicular lines is -1. However, we’ll need to check for the possible points out of the four points we’ll get in the end by this method.

O(2nlogn + 2n^2 * log n) using Binary search after sorting gave TLE on my implementation. So I used 3 hash tables (to avoid collisions - though one good/unique hash funcn is enough) to get AC with O(n^2 + 3n). IMO, this should have been tagged easy-med.

I followed a quite naive procedure to discover two line segments of equal lengths sharing a common end point and then checked if they are perpendicular or not.

If they are then see if the vertex that forms a square with the above line segments exists or not.

I really expected a TLE on the solution but somehow got AC in the first go.

my solution

It was sad to see wrong answers being accepted.

Many accepted solution gave answer 2 for the following test case-

4

0 1

1 0

0 -1

-1 0

The answer is obviously 0. My ac solution this gives 2!

3 Likes

Hello coders,

Hope you all are doing well.

I solved this problem during the contest but still wants to ask something .

I divide the problem in the following two cases.

1 . When the selected points are on the same axis (either on axis parallel to y axis or on axis parallel to x axis). This case was very easy to handle.

  1. Now, when the points are not on the same axis . I thought there will only exist a square if the two points are diagonally related it means that there are on the axis which is at 45 degree to the X axis or Y axis. I worked out this for number of example and finds that the observation is correct. Is this ??

I also want to ask you that suppose we have a set containing these two points (1,1) , (2,3) then any one can tell me rest two points with which these two points can form a square.(I think real points were not allowed. Am i missing somewhere Please help!!).

1 Like

My code is giving right answer for all possible cases. Still here it saying wrong answer. Please tll me why. my code is here. QYh1da - Online C Compiler & Debugging Tool - Ideone.com

#include <stdio.h>
#include <stdlib.h>
void quick_sort(long long int arr[],long long int y[],int low,int high)
{
int pivot,j,i;
long long int temp;
if(low<high)
{
pivot = low;
i = low;
j = high;

while(i<j)
{
while((arr[i]<=arr[pivot])&&(i<high))
{
i++;
}

while(arr[j]>arr[pivot])
{
j–;
}

if(i<j)
{
temp=arr[i];
arr[i]=arr[j];
arr[j]=temp;
temp=y[i];
y[i]=y[j];
y[j]=temp;
}
}

temp=arr[pivot];
arr[pivot]=arr[j];
arr[j]=temp;
temp=y[pivot];
y[pivot]=y[j];
y[j]=temp;
quick_sort(arr,y,low,j-1);
quick_sort(arr,y,j+1,high);
}
}
int find(long long int a[],int e,long long int m){
int l=0,u=e,c=-1;
while(l<=u){
int mid=(l+u)/2;
if(m==a[mid]){
if(mid==0){c=0;break;}
else if(a[mid-1]<m){c=mid;break;}
else u=mid-1;
}
else if(m<a[mid]){
u=mid-1;
}
else
l=mid+1;
}
// printf(“( %lld %d) \n”,m,c);
return c;
}
int main(void) {
int n;
scanf(“%d”,&n);
if(n<3){
printf(“%d\n”,4-n);
return 0;
}
long long int x[n],y[n];
int i,j;
for(i=0;i<n;i++){
scanf(“%lld %lld”,&x[i],&y[i]);
// printf(" %lld %lld",x[i],y[i]);
}
// printf(“\n”);
quick_sort(x,y,0,n-1);
/* for(i=0;i<n;i++){
printf(" %lld %lld",x[i],y[i]);
} */
// printf(“\n”);
int f1=-1,f4=-1,min=n,cnt=0,f2=0,f3=0;
for(i=0;i<n-1;i++){
for(j=i+1;j<n;j++){
long long int x1=x[i]-(y[j]-y[i]);
long long int y1=y[i]-(x[i]-x[j]);
long long int x4=x[j]-(y[j]-y[i]);
long long int y4=y[j]-(x[i]-x[j]);
f1=find(x,n-1,x1);
f4=find(x,n-1,x4);
// printf(“(%lld %lld %lld) (%lld %lld %d) \n”,x1,y1,y[f1],x4,y4,y[f4]);
if((f1>=0)&&(y[f1]==y1))f2=1;
else if(f1>=0){
int id=f1;
while(x[id]==x1){
if(y[id]==y1){f2=1;break;}
id++;
}
}
else f2=0;
//if(f2)printf(“%lld %lld %lld %lld \n”,x[i],y[i],x1,y1);
if((f4>=0)&&(y[f4]==y4))f3=1;
else if(f4>=0){
int id=f4;
while(x[id]==x1){
if(y[id]==y1){f3=1;break;}
id++;
}
}
else f3=0;
// if(f2)printf(“%lld %lld %lld %lld \n”,x[j],y[j],x4,y4);
if(f2&&f3){min=0;break;}
else if((f2&&!(f3))||(f3&&!(f2))){cnt=1;}
else{cnt=2;}
if(cnt<min)min=cnt;
}
if(f2&&f3)break;
}
printf(“%d\n”,min);
return 0;
}

When 3 or more points are given, for every 2 points shouldn’t we also consider the square with these points as the diagonal ? So shouldn’t 3 squares be checked ?
Or does considering the 2 squares include this square as well ? I’m confused

I did the searching using a hashtable. CodeChef: Practical coding for everyone

1 Like

great, you made a class and struct to implement hashtable. You could have use a map of int to int for the same :slight_smile:

Hey, I also did the O(N^2) approach with the hash table, but i still got TLE, can someone please tell me why?? CodeChef: Practical coding for everyone

oh, and I aslo used make_pair for hash table with two keys, could that be the reason for TLE??

Yes that thought struck me later. After i solved the problem :stuck_out_tongue: so just took it easy :stuck_out_tongue:

Can someone help me find what’s wrong with this approach ?

Here, each edge i.e. each pair of points was considered, note that for one line as an edge, there can be two possible squares, using vector algebra, the coordinates of the “supposed to be squares” were calculated, and then checked whether they were present via a hash table

1 Like

Actually the above approach considers the two points to be the edge of a square and then finds out the remaining two using rotation theorem. You must understand that there can be many possible ways to solve a problem. Even i solved it by considering them to be diagonal points as its easier and eliminates unnecessary searches. So just that!

1 Like

@djdolls The practice link above links to the contest problem.

Solved my problem from here TLE in CHEFSQUA - general - CodeChef Discuss .
Never knew even this [] and insert difference can change a TLE to AC!! Nice and easy question…

I agree the ‘easy’ difficulty does not justify the problem. :smiley: