题解
第一种情况
采用贪心的方法求得最优解。因为修改后的元素可能是原序列中没有出现过的元素。如果修改的某一列的元素是原序列中没有出现过的元素,那么这种情况下一定可以用贪心的办法求出最优解,做法是将众数最小的一列中的每个数变成一个全新的,该列中没有出现的,使得每个周期内的元素的异或和为0的数。
第二种情况
采用dp的方法求得最优解在这种情况下,由于没有最终修改后的元素是原数组中存在的数,因此可以从前往后枚举每一列,然后枚举选择第几行的数作为这列元素修改后的元素,由于异或具有交换性质,因此不具有顺序的问题,所以可以采用dp的方法递推出将序列变成数组中本来存在的某个数的情况。边界,f[0][0] = 0,目标状态是f[k][0],状态表示f[i][j]为前i列异或和为j的情况下的最小值
class Solution {
public:
// 1.某一列用了一个全新的数
// 2.每一列用了原来的数
const int N = 1024, INF = 1e8;
int s[1024]; // 求众数
int minChanges(vector<int>& nums, int k) {
int n = nums.size(), m = (n + k - 1) / k;
vector<vector<int>> f(k + 1, vector<int>(N, INF));
int cnt = 0, minv = INF; // 每一列代价
// f[i][j] 第i列的异或和为j
f[0][0] = 0;
for (int i = 1; i <= k; i++) {
int len = m;
memset(s, 0 , sizeof s);
if (n % k && n % k < i) len--;
for (int j = 0; j < len; j ++) {
s[nums[j * k + i - 1]]++;
}
int maxv = 0;
for (int j = 0; j < N; j++) {
maxv = max(maxv, s[j]);
}
cnt += len - maxv;
minv = min(minv, maxv); // 众数最少的那一列 不用众数 而用全新的数
for (int j = 0; j < N; j++) { // 异或和为j
for (int u = 0; u < len; u++) { // 每一行
int x = nums[u * k + i - 1], cost = len - s[x];
f[i][j] = min(f[i][j], f[i - 1][j ^ x] + cost);
}
}
}
// cnt: 每一列的代价
// minv表示 某一列不用众数时的代价 si - maxv -> si 变成全新的数代价
return min(cnt + minv, f[k][0]);
}
};