• + 11 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.

    • + 0 comments

      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!

    • + 2 comments

      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;

          if(2*k>n){ k=n-k; count =k;}
         long ar[] = new long[n];
          for(int i=0; i<n; i++)
              ar[i]=in.readLong();
          Arrays.sort(ar);   //Array sorted
          long[] set1 = new long[k];
          long[] set2 = new long[n-k];
          int ptrl=n/2, ptrr=ptrl, sptr1=0, sptr2=0;
          set1[sptr1++]=ar[ptrl]; count--;
          ptrl-=2; ptrr+=2; 
          while(count>0){
              set2[sptr2++]=ar[ptrl+1];
              set2[sptr2++]=ar[ptrr-1];
              set1[sptr1++]=ar[ptrl];
              count--; ptrl-=2;
              if(count>0){
                  set1[sptr1++]=ar[ptrr];
                  count--;
              }
          }
          ptrl++;
          while(ptrl>=0){
              set2[sptr2++]=ar[ptrl];
              ptrl--;
          }
          int p=n-1;
          while(sptr2!=(n-k)){
              set2[sptr2++]=ar[p];
              p--;
          }
          //calculate diff
          long ans=0;
          for(int i=0; i<k; i++){
              for(int j=0; j<(n-k); j++){
                  ans+=(long)Math.abs(set1[i]-set2[j]);
              }
          }
          System.out.print(ans);
      }
      
      • + 1 comment

        EDIT : now i figured out the flaw in the code and it works.! Thanks for the idea! :D

        • + 1 comment

          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".

          • + 0 comments

            YEah i figured it out... Thanks for explanation :)

      • + 0 comments

        it is not passing every test cases it goes fail.

    • + 0 comments

      Very nice inference drawn. Good Man !

    • + 2 comments

      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.

      • + 0 comments

        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.

      • + 0 comments

        Please check out my O(nlogn) solution.

        https://www.hackerrank.com/challenges/fair-cut/forum/comments/737085

    • + 0 comments

      Majestic!

    • + 0 comments

      @zh2196 your idea is amazing! Thanks!

    • + 0 comments

      My solution based on @zh2196 's approach.It passes all the test cases.

      //the numbers can be very large,so we take long
           static ArrayList<Long>li;
           static ArrayList<Long>lu;
          static long fairCut(int k1, ArrayList<Long> arr) {
              li=new ArrayList<Long>();
              lu=new ArrayList<Long>();
              Collections.sort(arr);
              System.out.println("sorted list is:"+arr);
              int n=arr.size();
              int k=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 difference
              if(k==1){
      li.add(arr.get(n/2));
      arr.remove(n/2);
      return calcResult(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 rightPointer
      
              int loc=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 direction
      
      loc1=loc1-2;
      loc2=loc2+2;
      //adding 2 elements,one from both directions into li list
      li.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 obtained
      return calcResult(li,arr);
      
      
      else if (k==1){//now one element more is to be added 
      
      //we will have to choose an element that gives minimum result
      long x1=loc1-2;//x1 is index position of the element after skipping one //element from the current position in the left direction.
      
      long x2=loc2+2;
      
      long valueReplaced=-1;
      long res1=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 x1
      li.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 list
      arr.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 state
      
      li.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 if
      
      System.out.println("li list is:"+li);
      System.out.println("arr list is:"+arr);
      return Math.min(res1,res2);
      //return the minimum value obtained by adding elements at position 
      //x1 or x2 to the li list
      }//end of else if
      return 0;
          }//end of func
      
      public static long calcResult(ArrayList<Long> liArr,ArrayList<Long> luArr){
      
      long sum=0;
      for(int i=0;i<liArr.size();i++){
          for(int j=0;j<luArr.size();j++){
              if(luArr.get(j)==0)//do not calculate for this as it has been added to //li list
              continue;
              else
              sum+=Math.abs(liArr.get(i)-luArr.get(j));
          }
      }
      return sum;
      }
      
    • + 1 comment

      Brilliant idea @zh2196! Here's my implementation in Python (passes all test cases):

      def compute_score(group_1, group_2):
          score = 0
          for i in group_1:
              for j in group_2:
                  score += abs(i - j)    
          return score
      
      def fairCut(k, arr):
          arr.sort()
          
          if 2 * k > n:
              k = n - k
          
          start = (len(arr) - 2 * k) // 2
          stop = start + 2 * k
          group_1, group_2 = [], []
      
          for i in range(len(arr)):
              if stop >= i >= start and (i - start) % 2 == 1:
                  group_1.append(arr[i])
              else:
                  group_2.append(arr[i])
          
          return compute_score(group_1, group_2)
      
      • + 0 comments

        Thanks for the code. Just wanted to pointed out, it the same as you do:

        mid = (n//2);
        start = mid-k;
        stop = mid+k;
        
    • + 0 comments

      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

    • + 0 comments

      0010101000 why not this? this is also close to middle