Algorithm analysis course design (7) Marble segmentation problem (multiple backpacks)

topic

There are several marbles with different sizes and aesthetics. In order to divide these marbles objectively, we need to give these marbles a score. The scores are divided into 6 grades, which are represented by numbers from 1 to 6. Now I hope to divide these marbles into two parts so that the sum of the scores of each part is the same.

enter:

Enter a line, including 6 numbers, which are the number of marbles of each grade. The number of marbles of each grade does not exceed 20,000.

Output:

If these marbles can be divided into two parts with the same evaluation level, then output true, otherwise output false.

Sample input:

1 0 1 2 0 0

Sample output:

false  

Claim

1. Write the solution process when the solution sample is input.

2. Write the algorithm analysis process, write a program to solve the above problems, and analyze the time complexity of the algorithm.

Reference link

This is Leikou's explanation on the 01 backpack. Once you understand it, you will know how to do it.

https://leetcode-cn.com/problems/partition-equal-subset-sum/solution/fen-ge-deng-he-zi-ji-by-leetcode-solution/ 

analysis

This question is a problem of multiple backpacks. As long as you know 01 backpack, you can do multiple backpacks. Therefore, we do not use recursion, but use dynamic programming. Just fill in a two-dimensional form, let alone why.

The question input is "1 0 1 2 0 0", that is, there is 1 marble of grade 1, 1 marble of grade 3, and 2 marbles of grade 4. Convert it into an array of marbles: num=[1, 3, 4, 4]. To be divided into two halves, each part is (1+3+4+4)/2=6 levels. Using these four numbers to make up 6, we can return true.

Now to build a two-dimensional table, we use the two-dimensional array dp[][] to represent it. The row is the element of the marble array num, the column is the level to be collected, and the table judges whether it is true or false. If we only make up 6, why should we write all 1 to 5? I'll talk about it later.

on one 1 2 3 4 5 6
1            
3            
4            
4            

Now start filling out the form line by line.

1. When filling in the first line, the first space means that there is only 1 in the backpack. It is obviously possible to make up 1, and dp[num[0]][1] is true. The next 2 to 6 can't be made up, they are all false.

2. When filling in the second line, note that it does not mean that there is only 3 in the backpack, but 1 and 3.

3. When filling in the third row, it means there are 1, 3, 4 in the backpack.

4. When filling in the fourth line, it means there are 1, 3, 4, 4 in the backpack

Therefore, why do you need to list all 1-5 when you make up 6, just to check if there are other marbles in the backpack that can make up a certain number with the current marble.

We can always only fill in one T in the first row, that is, when row=column, because a number can only make up its own number. When filling in the second row, for columns less than 3, directly copy the previous row; for columns equal to 3, directly fill in T; for columns greater than 3, use the difference obtained by subtracting 3 from the column to check the previous row and the column is equal to This difference grid (that is, whether there is a number equal to this difference in the backpack), if it is T, it means there is this number in the backpack, fill in T, if it is F, it means there is no such number in the backpack, fill in F.

The complete code is as follows: (Time is running out, no comments, you will know how to write when you think about filling in the form)

#include<stdio.h>
#include<math.h>
#include<stdlib.h>
#include<string.h>
#define MAXN 120000
bool distribution(int* num, int numSize);  
int main()
{   
    int num[MAXN]={0};
    int n;
    int cnt=0; 
    // 输入6个数
    for(int i=1;i<=6;i++)
    {
        scanf("%d",&n);
        // 多重背包数组转01背包数组,也就是每个大理石的等级组成的数组
        for(int j=0;j<n;j++)
        { 
            num[cnt]=i;
            cnt++;
        }
    }
    // 大理石数组,和它的长度传进去
    if(distribution(num,cnt))
       printf("true");
    else
       printf("false");
    return 0;
}   
// 填表
bool distribution(int* num, int numSize)
{
    // 只有1块大理石凑啥啊还,裂成两半吧
    if (numSize < 2) {
        return false;
    }

    int sum = 0, maxNum = 0;
    // 大理石的等级加起来
    for (int i = 0; i < numSize; ++i) {
        sum += num[i];
        maxNum = fmax(maxNum, num[i]);
    }

    // 大理石的等级加起来是奇数凑啥啊还,走走走
    if (sum & 1) {
        return false;
    }

    // 大理石等级和的一半才是最终要凑出的数
    int target = sum / 2;

    // 啥,连最大那块大理石都比目标数大?凑啥啊还
    if (maxNum > target) {
        return false;
    }

    // 表来了,就是它,二维数组dp
    int dp[numSize][target + 1];
    // 初始化dp为全F
    memset(dp, 0, sizeof(dp));

    // 填第一行,行和列相等的就是T
    dp[0][num[0]] = true;
    
    // 从第二行开始填
    for (int i = 1; i < numSize; i++) {
        int row = num[i];
        for (int j = 1; j <= target; j++) {
            // 列的数大于行的数,用列减去当前行的差,去找上一行列数等于这个差的状态
            if (j >= row) {
                dp[i][j] = dp[i - 1][j] | dp[i - 1][j - row];
            // 列数小于行数,直接照抄上一行
            } else {
                dp[i][j] = dp[i - 1][j];
            }
        }
    }
    // 返回表格右下角那个格,因为一旦上一行为T,后面所有行都为T
    return dp[numSize - 1][target];
}                              

The time complexity of the algorithm is O(n\cdot target)

This is not optimal, the optimal is to turn dp into a one-dimensional array. Because a grid is T, every row after the same column is T, so dp is not two-dimensional. The time complexity of this approach becomes O(target), the time relationship is no longer discussed.

Guess you like

Origin blog.csdn.net/qq_33514421/article/details/112430783