---
# System prepended metadata

title: 2172. Maximum AND Sum of Array
tags: [Leetcode, Dynamic Programming, Hard]

---

# 2172. Maximum AND Sum of Array
###### tags: `Leetcode` `Hard` `Dynamic Programming`
Link: https://leetcode.com/problems/maximum-and-sum-of-array/description/
## 思路
显然是一道要用状态压缩的dp题
一开始的想法是状态压缩nums 但是nums可能比numSlots大一倍 所以压缩numSlots更好
我们用3进制的state表示numSlots 如果第i位是0/1/2 说明第(i+1)个slot有0/1/2个数
于是dp[i][state]表达的是：用完第i个数字、配对了state所表示的slots时，所得的最大价值。所以核心代码大致会是：
```
 for (int i=0; i<nums.size(); i++)
    for (int nextState=0; nextState<pow(3, numSlots); nextState++)
      for (auto pickedSlot: prevState)
        dp[i][nextState] = dp[i-1][prevState] + (nums[i] & pickedSlot);
```
对于每一个number 我们遍历所有的nextState(也就是加上这个num之后的state) 然后我们要找到前一个state 然后算出前一个state+把当下number放到某一个slot里面使得state变成nextState所带来的收益
那么怎么找前一个state呢？找出nextState里面所有非空的slot 也就是pickedSlot 给这个slot里面的element数-1 就是前一个state了
### 优化
由于state里面已经涵盖了现在用了几个数字了 所以不需要第一层回圈

**注意: &的优先级比+低 line13一定要给(j+1)&num加括号**
## Code
### 思路一
```java=
class Solution {
    public int maximumANDSum(int[] nums, int numSlots) {
        int n = nums.length;
        int[][] dp = new int[n+1][(int)Math.pow(3, numSlots)];
        // Arrays.fill(dp[0], Integer.MIN_VALUE);
        // dp[0][0] = 0;
        int ans = 0;
        for(int i=1; i<=n; i++){
            int num = nums[i-1];
            for(int state=1; state<Math.pow(3, numSlots); state++){
                for(int j=0; j<numSlots; j++){
                    if(filled(state, j)){
                        dp[i][state] = Math.max(dp[i][state], dp[i-1][state-(int)(Math.pow(3, j))]+((j+1)&num));
                    }
                }
                if(i==n) ans = Math.max(ans, dp[n][state]);
            }
        }
        return ans;
    }
    private boolean filled(int state, int k){
        for(int i=0; i<k; i++){
            state/=3;
        }
        return state%3>=1;
    }
}
```
### 优化
```java=
class Solution {
    public int maximumANDSum(int[] nums, int numSlots) {
        int n = nums.length;
        int[][] dp = new int[n+1][(int)Math.pow(3, numSlots)];
        int ans = 0;
        for(int state=1; state<Math.pow(3, numSlots); state++){
            int temp = state;
            int count = 0;
            while(temp>0){
                count += temp%3;
                temp/=3;
            }
            int i = count;
            if(i>n) continue;  
            for(int j=0; j<numSlots; j++){
                if(filled(state, j)){
                    dp[i][state] = Math.max(dp[i][state], dp[i-1][state-(int)(Math.pow(3, j))]+((j+1)&nums[i-1]));
                }
            }
            if(i==n) ans = Math.max(ans, dp[n][state]);
        }
        return ans;
    }
    private boolean filled(int state, int k){
        for(int i=0; i<k; i++){
            state/=3;
        }
        return state%3>=1;
    }
}
```