State compression + dynamic programming (Blue Bridge Cup 2019 java group A candy problem)

foreword

Seeing this title shows that I am still ignorant, and the algorithm Xiaobai reported.
First of all, the reason for this problem is because, such a problem

Candy time limit: 1.0s Memory limit: 256.0MB Total score for this question: 25 points

[Problem description] The owner of the candy store sells a total of M flavors of candy. For the convenience of description, we number the M flavors 1 ∼ M.
  Xiao Ming wants to taste all flavors of candy. It's a pity that the boss doesn't sell candies individually, but sells them in packs of K pieces. Fortunately, the flavor of the K candies is marked on the candy package
, so Xiao Ming can know the flavor of the candy in each package before buying. Given N packs of candy, please calculate the minimum number of packs that Xiao Ming buys, so that he can taste all flavors of candy.

【Input format】 The first line contains three integers N, M and K. Next N lines, each line contains K integers T1, T2, · · · ,
TK, representing the flavor of a pack of candy.

[Output format] An integer represents the answer. If Xiao Ming cannot taste all the flavors, output −1.

【Sample input】

6 5 3  
1 1 2  
1 2 3  
1 1 3  
2 3 5  
5 4 2  
5 1 2  

【Example output】

2  

[Scale and conventions of evaluation use cases]

For 30% of the evaluation use cases, 1 ≤ N ≤ 20 .
For all evaluation samples, 1 ≤ N ≤ 100, 1 ≤ M ≤ 20, 1 ≤ K ≤ 20, 1 ≤ Ti ≤ M .

ideas

At that time, my first thought was an algorithm similar to a heuristic, using the idea of ​​"domination" to solve this problem, but this state is not easy to express, it should be a recursion after writing it, but the complexity It shouldn't be too different from dp, but that's not easy to do.

Dp Thought

So when we get here, we can naturally think of the knapsack problem first, and think of dp.
The requirement of the title is naturally, say, the number of packets of candy you want to eat (minimum number of packets).
So it is easy to think of a dp array dp[i][j] under the i package, in the j state (this state refers to how many candies are eaten now).
Then this d[i][j] = value According to this meaning, the value is i, which naturally realizes the dimension directly, then the dp we construct at this time refers to such dp[i]=j means in the i state Next, you need j packs of candy.
insert image description here

DP equation

Let's talk about the strategy of this dp first.
If the current state (for example, the current state means that you have eaten 4 kinds), the previous state is that you have eaten three kinds, and then you have taken 1 package
. Here it depends on the situation.
If, the current state There is no state saved in our original dp array, so naturally we can directly
take this package on the basis of the previous state.
If there is, and in this case, we have taken 3 packages, then obviously The number of packets corresponding to this state at this time should also be the number of packets obtained in the previous state + 1

Look at the specific code
insert image description here

How to save state

binary state save

We assumed so much earlier, the question is how do we represent this state?
Here is the state compression in the legend. How to directly turn a group of states into a number?

This is where the magic binary is needed.
How to do it, for example, this set of data
1 1 2
, because we have 5 flavors, so we can do this
. Our binary format is like this. 00000
The first number comes over and becomes like this
00001
The second number comes over, we | take or Operation so it is still
00001 and
then the third
00011

At this point, we convert it to decimal and get 3.
At this time, this 3 represents our state.

6 5 3    状态压缩
		二进制  十进制
1 1 2   00011	3
1 2 3	00111	7
1 1 3	00101	5


coding

Next coding solves the problem

class Main{
    
    
    private static int N;
    private static int M;
    private static int K;
    static boolean[][] vis = new boolean[105][22];
    public static void main(String[] args) {
    
    
        Scanner sc = new Scanner(System.in);
        N = sc.nextInt();// 多少包糖
        M = sc.nextInt();// 几种口味
        K = sc.nextInt();// 每包糖多少口味
        int[] w = new int[N+1];
        int[] dp=new int[1<<20];
        for (int i = 1; i <= N; i++) {
    
    
            for (int j = 0; j < K; j++) {
    
    
                int k = sc.nextInt();
                w[i] |=(1<<k-1) ;
                //存储每一包的状态,但是这个第一个数第一个状态不能改变,所以需要把k-1
                
                //这个的牛逼之处就是把一串状态数组,转化二进制(运算),之后变回十进制表示为一个数字表示状态
            }
        }
        Arrays.fill(dp, -1);//初始化-1,
        dp[0]=0;
        //dp的方程含义是下一个状态
        for(int i = 1;i<=N;i++)
            
            for(int s = 0;s < (1 << M);s++) {
    
    
                if (dp[s] == -1) continue;
                int nst = s | w[i];//下一个状态,比上一个状态多吃到一种糖果的状态(多吃一包)
                if (dp[nst] == -1 || dp[nst] > dp[s] + 1) dp[nst] = dp[s] + 1;
                //如果没有状态那么自然-1,如果有这个状态,并且这个状态不如上一个状态多吃一个那么就舍弃,直接上一个状态+1
            }
        System.out.println(dp[(1<<M)]);

    }

}

Summarize

I am a chicken...

Guess you like

Origin blog.csdn.net/FUTEROX/article/details/123362770