简体   繁体   中英

Find the minimum number of cuts

Can anybody give me a hint how to find efficient solution for the problem below?

Alex brought a roll to the kitchen, which he wants to share with his colleagues. To do this, he wants to cut the roll into N equal parts. Of course, the roll can only be cut across. Accordingly, Alex will make N − 1 cut with a knife at regular intervals.

Upon returning from the coffee break, Alex wondered - could it be possible to do with fewer movements, if Vanya's knife was infinitely long (in other words, if he could make as many cuts as he wanted at a time, if these cuts lie on one straight line)? It is believed that the places for the cuts are planned in advance, and all cuts are made with pinpoint precision.

It turns out that you can. For example, if Alex would like to divide the roll into 4 parts, he could do with two cuts - first he would divide the roll into two halves, and then combine the two halves and cut both in half at the same time. So I need to find the minimum of cuts.

Given N - people

6

Result

3

Given N - people

5

Result

3

I can do it with a small number of people, but what if there 100 or 1000 people?

My code below:

    public class Cake{
        public static void main(String[] args) throws IOException {
            BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
            int number = Integer.parseInt(reader.readLine());
            int all = 0;
            if (N % 2 == 0) {
                all = number/ 2;
            } 
            else {
            all = number / 2 + 1;
            }
                
            System.out.println(all);
        }
    }

It is indeed a math problem. You will need log(N,2) cuts for the first power of 2 people and one more for the extra people.

from math import log

def cutCount(N): return 0 if N<2 else int(log(N-1,2))+1

output:

for N in range(1,10): print(N,"people, cuts: ",cutCount(N))

1 people, cuts:  0
2 people, cuts:  1
3 people, cuts:  2
4 people, cuts:  2
5 people, cuts:  3
6 people, cuts:  3
7 people, cuts:  3
8 people, cuts:  3
9 people, cuts:  4

To verify for larger numbers, you can simulate the cuts mentally:

cutCount(50) # 6

1st cut --> 2 pieces
2nd cut --> 4 pieces
3rd cut --> 8 pieces
4th cut --> 16 pieces
5th cut --> 32 pieces
6th cut --> only cut 18 of the 32 pieces --> 50 pieces

Assuming you placed the pieces strategically every time, the last 18 pieces you need to cut are exactly 2/50th of the total length and the 14 others are 1/50th

there are several ways to do the "strategic" cuts. Here's an example:

 Number x Lengths (in 1/50th of the total length):
 
 cut #1 : [-----1x32-----][------------1x18---------------] total 2 pieces
 cut #2 : [-----2x16-----][------------2x9----------------] total 4 pieces
 cut #3 : [------4x8-----][---2x4--][--------2x5----------] total 8 pieces
 cut #4 : [------8x4-----][---4x2--][--2x2--][----2x3-----] total 16 pieces
 cut #5 : [-----16x2-----](------12x1-------)(-2x1-)[-2x2-] total 32 pieces
          (----------14x1--------)[--------18x2-----------]
 cut #6 : (----------14x1--------)(--------32x1-----------) total 50 pieces
          (---------------------50x1----------------------)

Brackets are not to scale. Pieces that are 1/50th are not cut any further

If you want a recursive version of this calculation, you can count 1 for every time you are able to divide the number of people by 2 and then add 1 if there was at least one odd result in these divisions:

def cutCount(N,odd=0):
    return odd if N<2 else 1+cutCount(N//2,odd|N%2)

Using Recursive algorithm

Note: Alain T answer is exact formula

Code

def cuts(n):
    '''
        Recursively decide number of required cuts '
        
        Algorithm:
            Base case: n = 0 or 1:
                no cuts, so answer is 0

             n is even: divide roll into two rolls of n//2 (1 cut).  
                       These two rolls can be cut in parallel in the future
                       so cuts will be 1 + cuts(n//2).

                       Since n is even, n//2 == (n+1)//2, 
                       so this can also be written as 1 + cuts((n+1)//2).
                
                       So answer is: 1 + cuts((n+1)//2)
        
             Odd n: divide roll into two rolls of lengths n//2 and (n+1)//2  (1 cut)
                      One of these is even, the other odd.
                      These two rolls can be cut in parallel successively 
                      until the longer 1 is reduced to length 1.
                    
                      Thus cuts is 1 + cuts(max((n//2), (n+1)//2))

                      For all n: (n+1)//2 >= (n//2), so
                               max((n//2), (n+1)//2) = (n+1)//2

                       So answer is 1 + cuts((n+1)//2)

             So we have:
                if n = 0 or 1: return 0
                otherwise: return 1 + cuts((n+1)//2)
    '''
    return 0 if n < 2 else 1 + cuts((n+1)//2)
        
    for n in list(range(9)) + [20, 50, 100, 1000, 10000]:
        print(f'{n} people, cuts: {cuts(n)}')

Output

0 people, cuts: 0
1 people, cuts: 0
2 people, cuts: 1
3 people, cuts: 2
4 people, cuts: 2
5 people, cuts: 3
6 people, cuts: 3
7 people, cuts: 3
8 people, cuts: 3
20 people, cuts: 5
50 people, cuts: 6
100 people, cuts: 7
1000 people, cuts: 10
10000 people, cuts: 14

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM