1
votes

This is one of the problems from the 2015 ICPC NorthWest Programming Contest and I was wondering if there is any easier or more efficient way of doing it.

Here's the problem:

"Fred likes to wear mismatched socks. This sometimes means he has to plan ahead. Suppose his sock drawer has one red, one blue, and two green socks. If he wears the red with the blue, he's stuck with matching green socks. He put together two mismatched pairs if he pairs red with green and then blue with green. Given the contents of his sock drawer, how many pairs of mismatched socks can he put together?"

Here's a sample input:

Color 1 -> 4 socks

Color 2 -> 3 socks

Color 3 -> 7 socks

Color 4 -> 11 socks

Color 5 -> 4 socks

The way I'm doing is I first read the input into an array and sort it increasingly. That way I'll have the highest number of socks at the end of the array. From here I basically compare arr[i] and arr[i-1] and get the min between them. Add that to the total, save the remainder and just repeat the process going down the array. For example, using the sample input it looks something like this:

Sorted array: [3,4,4,7,11]

1:3 socks ---> 1:3 socks ---> 1:0 socks ---> 1:0 socks

2:4 socks ---> 2:4 socks ---> 2:1 socks ---> 2:1 socks

3:4 socks ---> 3:4 socks ---> 3:0 socks ---> 3:0 socks

4:7 socks ---> 4:0 socks ---> 4:0 socks ---> 4:0 socks

5:11 socks ---> 5:4 socks ---> 5:0 socks ---> 5:0 socks

------> total = 14 possible combinations of mismatched socks. This seems way too naive an approach. Does anyone have any ideas on how to optimize it ? I can post my code for this if necessary.

3
I think this method is incorrect because you're not subtracting the lowest count from the highest count. Consider 1, 20, 80, 81 - after 1st iteration you'd get 1, 20, 0, 1 and be unable to do much with the 20, and get the answer 82 with 18 socks left over (where the correct answer should've been 91 with no socks left over).Brendan
@Brendan, that's not correct. In the example you gave the answer would be 82, and the remaining 18 socks would all be of the same color so they wouldn't count as part of the total. Or did I understand you wrong ?A curious one
D'oh. My method is incorrect too. For a correct answer; after 1st iteration it might be 0, 20, 80, 80, after 2nd iteration it might be 0, 10, 70, 80, and after 3rd iteration it might be 0, 0, 70, 70.Brendan

3 Answers

1
votes

I think the optimal solution can be found by examining all possible groupings of the different sock colors into 2 piles. For each such grouping p odd pairs of socks can be made, where p is the number of socks in the smallest pile. You want the grouping that gives the maximum p. You can generate all possible groupings of socks into 2 piles recursively.

Here's some Java code to illustrate:

public static void main(String[] args)
{
    int[] socks = {3,4,4,7,11};
    System.out.println(count(0, 0, socks, 0));
}

static int count(int a, int b, int[] socks, int i)
{
    if(i == socks.length)
    {
        return Math.min(a, b);
    }

    return Math.max(count(a+socks[i], b,          socks, i+1), 
                    count(a,          b+socks[i], socks, i+1));
}

Output:

14
1
votes

Build a graph data structure. Every sock is vertex. Make edges from every vertex to all vertices of another color.

Now find power of maximum matching - size of the set of edges without common vertices.

You can build max matching using Edmonds algorithm in polynomial time O(V^2*E). Seems that graph for this task would be dense, so complexity tends to O(V^4). There exists also Micali and Vazirani algorithm with lesser complexity (don't know about implementation hardness).

If your task doesn't require max matching itself - only number of edges, then that value might be calculated using randomized Lovasz algorithm based on Tutte's matrix theorem. (I did not find concise English description - perhaps terms might differ, short one in Russian is here)

1
votes

"HOPEFULLY CORRECT THIS TIME!" METHOD

Step 1: Check if there's 2 or more colours left. If there's none or one colour left you're finished (can't find more pairs).

Step 2: Find one colour that has the lowest non-zero count

Step 3: Excluding the colour with the lowest count (in case all colours have the same count); find the highest count and determine how many colours share the highest count

Step 4: Excluding the colour with the lowest count and all colours with the highest count; try to find the second highest count.

Step 5a: If there is a second highest count, calculate amount_to_pair = min(highest_count - second_highest_count, lowest_count).

Step 5b: If there isn't a second highest count, calculate amount_to_pair = lowest_count.

Step 6: Create amount_to_pair pairs by pairing socks from the colour with the lowest count with socks with colours that have the highest count as evenly as possible (e.g. if there's 9 red socks, 20 blue socks and 20 green socks; then create 5 "red and blue" pairs and 4 "red and green" pairs).

Step 7: Goto step 1.

Example (the pathological case mentioned in comments):

Initial condition

Color 1 -> 1 socks
Color 2 -> 20 socks
Color 3 -> 80 socks
Color 4 -> 81 socks

First iteration:

Color 1 -> 1 socks (lowest non-zero count)
Color 2 -> 20 socks
Color 3 -> 80 socks (2nd highest count)
Color 4 -> 81 socks (highest count)

Amount to remove = min(81-80, 1) = 1

Color 1 -> 1-1=0 socks (lowest non-zero count)
Color 2 -> 20 socks
Color 3 -> 80 socks
Color 4 -> 81-1=80 socks (highest count)

Results so far:
   (1 pair of colour 1 and colour 4)

Second iteration:

Color 1 -> 0 socks
Color 2 -> 20 socks (lowest non-zero count)
Color 3 -> 80 socks (highest count)
Color 4 -> 80 socks (highest count)

Amount to remove = 20

Color 1 -> 0 socks
Color 2 -> 20-20=0 socks (lowest non-zero count)
Color 3 -> 80-(20/2)=70 socks (highest count)
Color 4 -> 80-(20-20/2)=70 socks (highest count)

Results so far:
   (1 of colour 1 and colour 4)
   (10 of colour 2 and colour 3)
   (10 of colour 2 and colour 4)

Third iteration:

Color 1 -> 0 socks
Color 2 -> 0 socks
Color 3 -> 70 socks (lowest non-zero count)
Color 4 -> 70 socks (highest count)

Amount to remove = 70

Color 1 -> 0 socks
Color 2 -> 0 socks
Color 3 -> 70-70=0 socks (lowest non-zero count)
Color 4 -> 70-70=0 socks (highest count)

Results so far:
   (1 of colour 1 and colour 4)
   (10 of colour 2 and colour 3)
   (10 of colour 2 and colour 4)
   (70 of colour 3 and colour 4)

ORIGINAL METHOD

WARNING: The method below gives wrong results in various pathological cases (and has been updated/replaced by the algorithm above). I've left it here to give context to some of the comments

Starting condition:

Color 1 -> 4 socks
Color 2 -> 3 socks
Color 3 -> 7 socks
Color 4 -> 11 socks
Color 5 -> 4 socks

Find the highest count and lowest count; and cancel out whichever has lowest count so it ceases to exist:

Color 1 -> 4 socks
Color 3 -> 7 socks
Color 4 -> 11-3=8 socks
Color 5 -> 4 socks

Results so far:
   (3 of colour 2 and colour 4)

Do it again:

Color 3 -> 7 socks
Color 4 -> 8-4=4 socks
Color 5 -> 4 socks

Results so far:
   (3 of colour 2 and colour 4)
   (4 of colour 1 and colour 4)

Do it again:

Color 3 -> 7-4=3 socks
Color 5 -> 4 socks

Results so far:
   (3 of colour 2 and colour 4)
   (4 of colour 1 and colour 4)
   (4 of colour 4 and colour 3)

Do it again:

Color 5 -> 4-3=1 sock

Results so far:
   (3 of colour 2 and colour 4)
   (4 of colour 1 and colour 4)
   (4 of colour 4 and colour 3)
   (4 of colour 3 and colour 5)

Stop because there's only one colour left.