We use cookies to ensure you have the best browsing experience on our website. Please read our cookie policy for more information about how we use cookies.
I figured out the greedy strategy of this problem! The optimal cut is independent of the numbers, but only depends on their order. For example, if there are N=10 numbers to cut, and one person gets K=3 numbers, the best cut will be 0001010100, which means we first sort the 10 numbers in ascending order A[1]<=A[2]<=...<=A[10], and give the 0s to one person and the 1s to another. The strategy is to place the 01-string repetition (the "010101" in the example) as close to the middle of the whole string as possible. If K*2>N, redefine K to be N-K (switching the 0s and 1s). The intuition behind this greedy strategy comes from physics. The unfairness is like an interaction potential to be minimized. The 0s and 1s attract each other and form an ionic crystal in the middle of a positive charge background.
Your idea didn't work. I did the same like firstly sorted the array, then placed element at n/2(middle element) in set1 and then alternately in set2 and set1 from both sides of middle but only a few test cases could pass!
I posted the greedy idea after passing all test cases. So it should be trustable. The intuition of physics can be justified using imagined incremental changes to rule out non-optimal choices. Then the optimal choice is one that is in "equilibrium".
Thanks for the great idea!I wrote a solution with it and solved the problem ( unfortunately after I unlocked the editorial :D ).
I want to ask you is there any way to compute the final answer in
way faster than N^2. Because it is easy to divide the sequence into 2 sets, but the computing of the answer bottlenecks the algorithm.
I looked at my submitted code and found that I did the optimal strategy in O(N) after the O(N*log(N)) sorting but the value of the objective function lazily in O(N^2) to make the code short. I believe I can do the objective function in O(N) after sorting as well, because
S=sum_{i,j=1..N} |A[i]-A[j]|
can be calculated in O(N) after sorting using the partial sum trick (beware of overflow). One may then apply this method three times to all numbers to get S, the numbers given to Li to get S1, and the numbers given to Lu to get S2. Then (S-S1-S2)/2 should give the answer.
My solution based on @zh2196 's approach.It passes all the test cases.
//the numbers can be very large,so we take longstaticArrayList<Long>li;staticArrayList<Long>lu;staticlongfairCut(intk1,ArrayList<Long>arr){li=newArrayList<Long>();lu=newArrayList<Long>();Collections.sort(arr);System.out.println("sorted list is:"+arr);intn=arr.size();intk=Math.min(k1,n-k1);//li will always get the element at position at n/2// (doesnt matter whether n is even or odd)//if k is 1 then give li n/2th element and find absolute differenceif(k==1){li.add(arr.get(n/2));arr.remove(n/2);returncalcResult(li,arr);}//if k is greater then 1,then we must make the 01010101 //combination//assume that li denotes 0 and lu denotes 1//now to ensure the combination,li must skip one element and take //the immediately following element li.add(arr.get(n/2));//loc1 is a leftPointer and loc2 is rightPointerintloc=n/2,loc1=n/2,loc2=n/2;arr.set(n/2,(long)0);//change the position at n/2 to 0//this means that the element at this position has been given to li//then in the calcResult function,continue when this 0 is //encountered--k;while(k>1){//skipping elements is possible in both directions,left and right//loc1 will take every "other" element in the left direction//(this means that it will take element after skipping one //element) similarly loc2 will take every other element in right directionloc1=loc1-2;loc2=loc2+2;//adding 2 elements,one from both directions into li listli.add(arr.get(loc1));li.add(arr.get(loc2));arr.set(loc1,(long)0);arr.set(loc2,(long)0);k-=2;//two more elements were addded to li's list so decrement k count}if(k==0)//all the requisite elements have been obtainedreturncalcResult(li,arr);elseif(k==1){//now one element more is to be added //we will have to choose an element that gives minimum resultlongx1=loc1-2;//x1 is index position of the element after skipping one //element from the current position in the left direction.longx2=loc2+2;longvalueReplaced=-1;longres1=Long.MAX_VALUE,res2=Long.MAX_VALUE;if(x1>=0)//the position should ,ofcourse be >=0 to find it in the given list{//now we are calculating result using x1li.add(arr.get((int)x1));valueReplaced=arr.get((int)x1);//this variable will be used later//it stores the value that will be replaced in the original array listarr.set((int)x1,(long)0);System.out.println("li arr is:"+li+"and arr is:"+arr);res1=calcResult(li,arr);//res1 has the result when element at x1 position //is chosen}if(x2<=arr.size()-1)//x2 should evidently be <=0 to find it//in the given arrayList{System.out.println("li array is:"+li+"and original array is:"+arr);if(valueReplaced==-1){//x1 is less than 0,no changes took place//so we continue normally li.add(arr.get((int)x2));arr.set((int)x2,(long)0);res2=calcResult(li,arr);//res2 is the result obtained when we choose element at x2 in the li list}else{//element at position x1 was used to find the result//so we must restore the list to its previous stateli.remove(li.size()-1);arr.set((int)x1,valueReplaced);li.add(arr.get((int)x2));arr.set((int)x2,(long)0);res2=calcResult(li,arr);}}//end of x2 ifSystem.out.println("li list is:"+li);System.out.println("arr list is:"+arr);returnMath.min(res1,res2);//return the minimum value obtained by adding elements at position //x1 or x2 to the li list}//end of else ifreturn0;}//end of funcpublicstaticlongcalcResult(ArrayList<Long>liArr,ArrayList<Long>luArr){longsum=0;for(inti=0;i<liArr.size();i++){for(intj=0;j<luArr.size();j++){if(luArr.get(j)==0)//do not calculate for this as it has been added to //li listcontinue;elsesum+=Math.abs(liArr.get(i)-luArr.get(j));}}returnsum;}
Fair Cut
You are viewing a single comment's thread. Return to all comments →
I figured out the greedy strategy of this problem! The optimal cut is independent of the numbers, but only depends on their order. For example, if there are N=10 numbers to cut, and one person gets K=3 numbers, the best cut will be 0001010100, which means we first sort the 10 numbers in ascending order A[1]<=A[2]<=...<=A[10], and give the 0s to one person and the 1s to another. The strategy is to place the 01-string repetition (the "010101" in the example) as close to the middle of the whole string as possible. If K*2>N, redefine K to be N-K (switching the 0s and 1s). The intuition behind this greedy strategy comes from physics. The unfairness is like an interaction potential to be minimized. The 0s and 1s attract each other and form an ionic crystal in the middle of a positive charge background.
Your idea didn't work. I did the same like firstly sorted the array, then placed element at n/2(middle element) in set1 and then alternately in set2 and set1 from both sides of middle but only a few test cases could pass!
My code in java :- public static void main(String[] args) { InputReader in = new InputReader(System.in); int n=in.readInt(), k=in.readInt(), count=k;
EDIT : now i figured out the flaw in the code and it works.! Thanks for the idea! :D
I posted the greedy idea after passing all test cases. So it should be trustable. The intuition of physics can be justified using imagined incremental changes to rule out non-optimal choices. Then the optimal choice is one that is in "equilibrium".
YEah i figured it out... Thanks for explanation :)
it is not passing every test cases it goes fail.
Very nice inference drawn. Good Man !
Thanks for the great idea!I wrote a solution with it and solved the problem ( unfortunately after I unlocked the editorial :D ).
I want to ask you is there any way to compute the final answer in way faster than N^2. Because it is easy to divide the sequence into 2 sets, but the computing of the answer bottlenecks the algorithm.
I looked at my submitted code and found that I did the optimal strategy in O(N) after the O(N*log(N)) sorting but the value of the objective function lazily in O(N^2) to make the code short. I believe I can do the objective function in O(N) after sorting as well, because
S=sum_{i,j=1..N} |A[i]-A[j]|
can be calculated in O(N) after sorting using the partial sum trick (beware of overflow). One may then apply this method three times to all numbers to get S, the numbers given to Li to get S1, and the numbers given to Lu to get S2. Then (S-S1-S2)/2 should give the answer.
Please check out my O(nlogn) solution.
https://www.hackerrank.com/challenges/fair-cut/forum/comments/737085
Majestic!
@zh2196 your idea is amazing! Thanks!
My solution based on @zh2196 's approach.It passes all the test cases.
Brilliant idea @zh2196! Here's my implementation in Python (passes all test cases):
Thanks for the code. Just wanted to pointed out, it the same as you do:
I folllowed your approoach and simplified it to
int st=(n-1)/2, en=n/2; st = st-k+1; en = en+k-1;
Just choose alternate elements in this range for set1
put the remaining elments in set2
calculate sum
0010101000 why not this? this is also close to middle