Kaboobly Dooists visit the Canteen

Last Sunday, I had a $50 food coupon.

  • With it I could buy items from the canteen which costs at most $50.

  • I saw that I was so hungry that I had to buy exactly 5 items from the canteen.

  • However, the canteen had a strange rule. They partitioned their list of items in 2-tuples. You can only order at most one item from each tuple.

If the following is the list of the items labelled 'A' thru 'T' with the tuples contained in braces, in how many ways can I order the items?

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
[{'A': 12, 'B': 7},
{'C': 10, 'D': 9},
{'E': 7, 'F': 13},
{'G': 11, 'H': 9},
{'I': 8, 'J': 13},
{'K': 10, 'L': 10},
{'M': 8, 'N': 11},
{'O': 11, 'P': 9},
{'Q': 7, 'R': 11},
{'S': 9, 'T': 12}]

Image updated by Calvin


The answer is 4957.

This section requires Javascript.
You are seeing this because something didn't load right. We suggest you, (a) try refreshing the page, (b) enabling javascript if it is disabled on your browser and, finally, (c) loading the non-javascript version of this page . We're sorry about the hassle.

6 solutions

Brock Brown
Jan 26, 2015

This looks like a job for breadth first search !

Python:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
canteen = 'ABCDEFGHIJKLMNOPQRST'
bad_pairs = set()
for i in xrange(0,len(canteen),2):
    new = (canteen[i],canteen[i+1])
    bad_pairs.add(new)
price = {'A':12,'B':7,\
    'C':10,'D':9,'E':7,\
    'F':13,'G':11,'H':9,\
    'I':8,'J':13,'K':10,\
    'L':10,'M':8,'N':11,\
    'O':11,'P':9,'Q':7,\
    'R':11,'S':9,'T':12}
def sum_price(items):
    total = 0
    for item in items:
        total += price[item]
    return total
def alphabetize(items):
    return ''.join(sorted(items))
def valid(items):
    for a, b in bad_pairs:
        if a in items and b in items:
            return False
    if sum_price(items) > 50:
        return False
    if items != alphabetize(items):
        return False
    return True
def neighbors(items):
    children = set()
    okay = set(canteen)-set(items)
    for item in okay:
        if valid(items+item):
            children.add(items+item)
    return children
level = {0:set([''])}
cur_level = 1
while cur_level <= 5:
    level[cur_level] = set()
    for items in level[cur_level-1]:
        for new in neighbors(items):
            level[cur_level].add(new)
    del level[cur_level-1]
    cur_level += 1
print "Answer:", len(level[5])

Evaluation time: 2.98 seconds

Calvin Lin Staff
Jan 26, 2015

Let the numbers be labelled as a 1 , a 2 , b 1 , b 2 , j 1 , j 2 a_1, a_2, b_1, b_2, \ldots j_1, j_2 .

Consider the polynomial ( 1 + x k 1 y + x k 2 y ) \prod ( 1 + x^{k_1} y + x^{k_2} y ) .

Then, the conditions tell us that we are interested in the sum of coefficients of the terms x k y 5 x ^ k y^ 5 where 0 k 50 0 \leq k \leq 50 . This is ugly, but easily computed.

We get that the answer is 4957.


A follow up question is, what approach has the least complexity / shortest run time as the number of lines approach infinity? My approach has an exponential run-time, though it's quite easy to understand and code up.

Dynamic programming. Let P [ i ] [ k ] [ b ] P[i][k][b] be the number of ways to choose k k items from the first i i tuples that cost exactly b b . Then, P [ 0 ] [ 0 ] [ 0 ] = 1 P[0][0][0]=1 and P [ i ] [ k ] [ b ] = P [ i 1 ] [ k ] [ b ] j P [ i 1 ] [ k 1 ] [ b A [ i ] [ j ] ] P[i][k][b]=P[i-1][k][b]\sum_{j}P[i-1][k-1][b-A[i][j]] if the items in the i i -th tuple have costs A [ i ] [ ] A[i][] . (The reason why this holds is quite clear: we either chose an item from the i i -th tuple or not; if we chose an item, the cost of all k 1 k-1 items chosen before it is b A [ i ] [ j ] b-A[i][j] .)

Complexity: O ( N K B ) O(NKB) if there are N N items in T T tuples, out of which we need to choose K K and the budget is B B , which is (pseudo)polynomial; the answer is b P [ T ] [ K ] [ b ] \sum_b{P[T][K][b]} .

Jakub Šafin - 6 years, 4 months ago

By ( 1 + x a 1 y + x a 2 y ) \prod ( 1 + x^{a_1} y + x^{a_2} y ) , do you mean ( 1 + x a 1 y + x a 2 y ) ( 1 + x b 1 y + x b 2 y ) ( 1 + x c 1 y + x c 2 y ) ( 1 + x j 1 y + x j 2 y ) ( 1 + x^{a_1} y + x^{a_2} y )( 1 + x^{b_1} y + x^{b_2} y )( 1 + x^{c_1} y + x^{c_2} y )\cdots( 1 + x^{j_1} y + x^{j_2} y )

Agnishom Chattopadhyay - 6 years, 4 months ago

Log in to reply

Yupz. I ran out of variables to use. Changed it to k 1 , k 2 k_1, k_2 and hopefully that makes more sense.

Though this approach is easy to understand, it get pretty tedious (even after working modulo x 50 , y 5 x^{50} , y^5 .

Calvin Lin Staff - 6 years, 4 months ago

This is great! Pretty much what @bobbym none did, when Nick asked him the same question here

Agnishom Chattopadhyay - 6 years, 4 months ago

This is the code I ran, which gives me 9676686 as the answer. I don't know what's wrong with it. Anyone can help me? Thanks.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
//Backpack Problem
// The "KabooblyDooistsVisitTheCanteen" class.
public class KabooblyDooistsVisitTheCanteen
{
    public static void main (String[] args)
    {

        int count = 0; //counts number of possibilities
        int money = 50;

        int price[] = {12, 7, 10, 9, 7, 13, 11, 9, 8, 13, 10, 10, 8, 11, 11, 9, 7, 11, 9, 12};

        for (int i = 0 ; i < 20 ; i++)
        {
            money -= price [i];

            //System.out.println (price[i]);

            for (int j = 0 ; j < 20 ; j++)
            {
                if (j / 2 == i / 2) //You can only order at most one item from each tuple.
                {
                    continue;
                }
                money -= price [j];
                //System.out.println (" "+price[j]);
                for (int k = 0 ; k < 20 ; k++)
                {
                    if (k / 2 == j / 2 || k / 2 == i / 2)
                    {
                        continue;
                    }
                    money -= price [k];
                    //System.out.println ("  "+price[k]);
                    for (int l = 0 ; l < 20 ; l++)
                    {
                        if (l / 2 == k / 2 || l / 2 == j / 2 || l / 2 == i / 2)

                            {
                                continue;
                            }
                        money -= price [l];
                        for (int m = 0 ; m < 20 ; m++)
                        {
                            if (m / 2 == l / 2 || m / 2 == k / 2 || m / 2 == j / 2 || m / 2 == i / 2)
                            {
                                continue;
                            }
                            money -= price [m];
                            for (int n = 0 ; n < 20 ; n++)
                            {
                                if (n / 2 == m / 2 || n / 2 == l / 2 || n / 2 == k / 2 || n / 2 == j / 2 || n / 2 == i / 2)
                                {
                                    continue;
                                }
                                money -= price [n];
                                if (money >= 0) // check if money spent is under or equal to $50
                                {
                                    count++;
                                }
                                money = 50;

                            }
                        }
                    }
                }

            }
        }

        System.out.print (count);

    } // main method
} // KabooblyDooistsVisitTheCanteen class 

Nathan Ko - 6 years, 4 months ago

Mathematica:

1
2
3
4
5
meyers[l_, k_, s_] := 
 Select[Flatten[Tuples /@ Subsets[Partition[l, 2], {k}], 1], 
   Total[#] <= s &] // Length

meyers[{12, 7, 10, 9, 7, 13, 11, 9, 8, 13, 10, 10, 8, 11, 11, 9, 7, 11, 9, 12}, 5, 50]

Haskell:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
data Player = Player Char Int

instance Show Player where
    show (Player name _) = show [name]

players = cutinto2 $ zipWith Player ['A'..'T'] [12, 7, 10, 9, 7, 13, 11, 9, 8, 13, 10, 10,
                                                                                  8, 11, 11, 9, 7, 11, 9, 12]

choose :: [b] -> Int -> [[b]]
_      `choose` 0       = [[]]
[]     `choose` _       =  []
(x:xs) `choose` k       =  (x:) `fmap` (xs `choose` (k-1)) ++ xs `choose` k

cutinto2 :: [b] -> [[b]]
cutinto2 [x,y] = [[x,y]]
cutinto2 (x:y:xs) = [[x,y]] ++ cutinto2 xs

value (Player _ x) = x

combies =  foldr (++) [] (sequence `map` choose players 5)

main = print $ length $ filter (\p -> sum(map value p) <= 50) combies

I updated the image. Is this more suitable?

Calvin Lin Staff - 6 years, 4 months ago

Log in to reply

Yes, Thanks.

I wonder why nobody solved the problem.

Agnishom Chattopadhyay - 6 years, 4 months ago

Log in to reply

Got it. :)

This took me like three hours to solve. Cool problem.

Brock Brown - 6 years, 4 months ago

Nice short Mathematica solution, I wrote mine in Python and I still had to use 48 lines. The Haskell solution gave me a good laugh.

Brock Brown - 6 years, 4 months ago
Nam Diện Lĩnh
Jun 22, 2015

Using brute force (code written in Boo):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
def c_recur(result, list, start, end, left) as void:
    if start>=end or left<=0:
        result.Add(list.ToArray());
        return;

    for i in range(start, end):
        list.Add(i);
        c_recur(result, list, i+1, end+1, left-1);
        list.Pop();

def c(k as int, n as int, start as int):
    if k<=0 or n<=0 or k>n:
        return [];

    result=[];

    c_recur(result, [], start, start+n-k+1, k);
    return result;

list=[];
list.Add((12,10,7,11,8,10,8,11,7,9))
list.Add((7,9,13,9,13,10,11,9,11,12))

count=0;

def order_recur(cm, itemLeft, moneyLeft):
    if moneyLeft<0:
        return;

    if itemLeft==0:
        count++;
        return;

    for i in range(0, 2):
        order_recur(cm, itemLeft-1, moneyLeft-list[i][cm[itemLeft-1]]);

for cm in c(5, 10, 0):
    order_recur(cm, 5, 50);

print count;

Chew-Seong Cheong
Jan 29, 2015

I use Python to quote.

 # Kaboobly Dooists visit the Canteen

 from itertools import combinations
 L=[[12,7],[10,9],[7,13],[11,9],[8,13],[10,10],[8,11],[11,9],[7,11],[9,12]]
 s = []; a = []; b = []; c = []; d = []; e = []
 n = 0
 for p in combinations("0123456789",5):
         a = L[int(p[0])]
     b = L[int(p[1])]
     c = L[int(p[2])]
     d = L[int(p[3])]
     e = L[int(p[4])]
     for i in range (2):
         for j in range (2):
             for k in range (2):
                 for l in range (2):
                     for m in range (2):
                         t = a[i]+b[j]+c[k]+d[l]+e[m]
                         if t <= 50:
                             n += 1
                             print n, t, a[i], b[j], c[k], d[l], e[m]

Calvin Lin But how do you go about doing this?

I did it using binary numbers. Binary numbers will give us all combinations of 1s and 0s. This property can be exploited to go through all the combinations in an orderly fashion. I only know C++ language, so I had to make up a lot of things for the implementation. I do not know how fast it runs. But it seems to run within a second or so.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
#include<fstream.h>
#include<conio.h>

class bigno
    {
    int *x,n,no;
    public:
    bigno(int dig)
        {
        n=dig;
        x=new int[n];
        for(int i=0;i<n;x[i]=0,i++);
        no=0;
        }
    void flash()
        {
        for(int i=0;i<n;x[i]=0,i++);
        }
    void adddig(int d,int i)
        {
        x[i]+=d;
        if(x[i]>=2)
            {
            x[i]=x[i]%2;
            adddig(1,i+1);
            }
        }
    void add()
        {
        adddig(1,0);
        }
    void disp(int a1)
        {
        for(int i=n-1;i>=0;i--)
            cout<<x[i];
        cout<<endl;
        }
    int count()
        {
        int count=0;
        for(int i=0;i<n;i++)
            if(x[i]==1)
                count++;
        return count;
        }
    int ret(int i)
        {
        return x[i];
        }
    };

int main()
    {
    clrscr();
    bigno G(10),B(5);
    long c=0;
    int ar[10][2];
    ifstream iff("item.txt");
    for(int b=0;b<10;b++)
        {
        iff>>ar[b][0];
        iff>>ar[b][1];
        }
    for(int i=0;i<1024;i++)
        {
        if(G.count()==5)
             {
             B.flash();
             for(int k=0;k<32;k++)
                 {
                 int cost=0,r=0;
                 for(int j=0;j<10;j++)
                      {
                      if(G.ret(j)==1)
                          {
                          cost+=ar[j][B.ret(r)];
                          r++;
                          }
                      }
                 if(cost<=50)
                     c+=1;
                 B.add();
                 }
             }
        G.add();
        }
    cout<<c;
    getch();
    iff.close();
    return 0;
    }

I am really interested in programming and CS. But the algorithms and methods used by many to solve problems here are way too hard for me to understand. Is there a way to learn these things easily? Any suggestions Agnishom Chattopadhyay ?

Log in to reply

  1. I'll suggest you first try to grasp algorithms which you can. There is no hurry. Introduction to Algorithms is a great read. Also, Brilliant Wiki Articles (Search Agnishom's Wikis ) and TopCoder Tutorials would be helpful. Even though the wiki is yet to be filled the map should be a helpful reference

  • Solve a lot of computer science problems. Also, solve a lot of problems in the computer science way. If possible, think of various ideas. Also, read through the solutions. You might want to try Project Euler, Hacker Rank, CodeChef, etc for cool programming problems.

  • Try analysing your code. Think why it is correct and how fast it runs. Think how it can be improved.

  • Assign yourself nice projects like maybe creating a game or an android app or a sudoku solver or a cool animation. That way you get better with coding.

  • Do not expect there to be comprehensive compact tutorials for every useful algorithm. Be desperate to code out ideas you see around.

  • Check Brian Bi's take on this

    If there is anything I can help you with, post a note and tag me

    Agnishom Chattopadhyay - 6 years, 3 months ago

    Log in to reply

    Thank you so much! Really appreciate the help!

    Raghav Vaidyanathan - 6 years, 3 months ago

    Log in to reply

    @Raghav Vaidyanathan no problem

    Agnishom Chattopadhyay - 6 years, 3 months ago

    0 pending reports

    ×

    Problem Loading...

    Note Loading...

    Set Loading...