lc
当前状态 k 选中了偶数个数字,则枚举其中一对数字 i,j
f[k ^ (1 << i) ^ (1 << j)]
把 k 消掉第 i 位的 1 以及第 j 位的 1
lc1799
dp+状压
预处理所有数对的最大公约数
状态压缩动态规划枚举数字选取状态,++计算选取偶数个数字时的最大配对得分++
最终返回选完所有数字的最大得分
定义状态: f[k] 表示选取状态为 k (二进制位表示数字是否被选中)时的最大得分
初始状态: f[0] = 0 (没有选中任何数字时得分为0),其余状态初始化为0
状态转移:若当前状态 k 选中了偶数个数字,则枚举其中一对数字 i,j ,从状态 k ^ (1 << i) ^ (1 << j) (去掉这对数字的状态)转移而来,加上当前配对的得分
遍历顺序:++按状态二进制中1的数量从小到大遍历++,确保计算当前状态时,依赖的前置状态已计算完成
答案输出:最终答案为 f[(1 << m) - 1] ,即所有数字都被选中时的最大得分。
class Solution {
public:
int maxScore(vector<int>& nums) {
int m = nums.size();
int g[m][m];
for (int i = 0; i < m; ++i) {
for (int j = i + 1; j < m; ++j)
++g[i][j] = gcd(nums[i], nums[j]);++
//pre-p
}
int f[1 << m];
memset(f, 0, sizeof f);
for (int k = 0; k < 1 << m; ++k) {
++int cnt = __builtin_popcount(k);++
if (cnt % 2 == 0) {
for (int i = 0; i < m; ++i) {
++if (k >> i & 1) {++
for (int j = i + 1; j < m; ++j) {
++if (k >> j & 1)
f[k] = max(f[k], f[k ^ (1 << i) ^ (1 << j)] + cnt / 2 * g[i][j]);++
}
}
}
}
}
++return f[(1 << m) - 1];++
}
};
lc995
贪心+滑窗
队列维护翻转区间的左端点,遍历数组时判断当前位置是否需要翻转,能翻转则记录左端点并计数,无法翻转则返回-1,最终得到最小翻转次数
class Solution {/++/贪心地从左到右依次翻转++
public:
int minKBitFlips(vector<int>& A, int K) {
int n=A.size(),ans=0;
queue<int> q;
for(int i=0;i<n;++i){
if(!q.empty()&& i-q.front()>=K) q.pop();
//维护与当前位置的距离<=K的翻转位置(左端点)
if((A[i]+q.size())%2==0){ //需要翻转
if(i+K>n) return -1; //无法翻转
q.push(i);
++ans;
}
}
return ans;
}
};
队列动态维护「当前位置被翻转了几次」,快速判断要不要翻转,从而用贪心策略找到最少翻转次数
++队列的大小 = 当前位置 i 被翻转的次数++
从左到右遍历,遇到 0 就翻转它开头的 K 位,因为如果现在不翻,后面就没机会了