一、题
1.数位和排序需要的最小交换次数
3551. 数位和排序需要的最小交换次数 - 力扣(LeetCode)
class Solution {
public:
int minSwaps(vector<int>& nums) {
int n = nums.size();
typedef pair<int, int> pii;
// 计算 x 的数位和
auto calc = [&](int x) {
int ret = 0;
for (; x; x /= 10) ret += x % 10;
return ret;
};
// 先按数位和排序,再按本身的大小排序
vector<pii> tmp;
for (int x : nums) tmp.push_back({calc(x), x});
sort(tmp.begin(), tmp.end());
// 把 nums 里的每个数改成它最终需要到达的下标
unordered_map<int, int> mp;
for (int i = 0; i < n; i++) mp[tmp[i].second] = i;
for (auto &x : nums) x = mp[x];
int ans = n;
bool vis[n];
memset(vis, 0, sizeof(vis));
// 计算图中有几个环
for (int i = 0; i < n; i++) if (!vis[i]) {
// 遍历包含节点 i 的环
ans--;
for (int j = i; !vis[j]; j = nums[j]) vis[j] = true;
}
return ans;
}
};
这题是可以任意两个元素交换,那如果是相邻元素呢?
2.相邻元素交换
求逆序对的方法
方法1:归并排序
#include <iostream>
using namespace std;
typedef long long LL;
const int N = 1e5 + 10;
int a[N], tmp[N];
LL merge_sort(int q[], int l, int r)
{
if (l >= r)
return 0;
int mid = l + r >> 1;
LL res = merge_sort(q, l, mid) + merge_sort(q, mid + 1, r);
int k = 0, i = l, j = mid + 1;
while (i <= mid && j <= r)
{
if (q[i] <= q[j])
tmp[k++] = q[i++];
else
{
res += mid - i + 1;
// q[i]>q[j]璇存槑鍦ㄥ乏杈瑰簭鍒梉l,mid]涓�鏈塵id-i+1涓�鏁�>q[j]
tmp[k++] = q[j++];
}
}
while (i <= mid)
tmp[k++] = q[i++];
while (j <= r)
tmp[k++] = q[j++];
for (i = l, j = 0; i <= r; i++, j++)
q[i] = tmp[j];
return res;
}
int main()
{
int n;
scanf("%d", &n);
for (int i = 0; i < n; i++)
scanf("%d", &a[i]);
cout << merge_sort(a, 0, n - 1) << endl;
}
方法2:树状数组
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
const int N = 100010; // 根据数据范围修改
int tr[N]; // 树状数组
int m; // 离散化后的最大值
int lowbit(int x) {
return x & -x;
}
void add(int x, int v) {
for (int i = x; i <= m; i += lowbit(i)) {
tr[i] += v;
}
}
int query(int x) {
int res = 0;
for (int i = x; i; i -= lowbit(i)) {
res += tr[i];
}
return res;
}
int countInversions(vector<int>& nums) {
int n = nums.size();
vector<int> arr(nums);
// 离散化
sort(arr.begin(), arr.end());
arr.erase(unique(arr.begin(), arr.end()), arr.end());
auto getId = [&](int x) {
return lower_bound(arr.begin(), arr.end(), x) - arr.begin() + 1; // 1-based
};
m = arr.size(); // 设定最大下标
// 清空树状数组
fill(tr, tr + m + 2, 0);
int ans = 0;
for (int i = n - 1; i >= 0; --i) {
int id = getId(nums[i]);
ans += query(id - 1); // 统计当前值左边有多少小于它的
add(id, 1); // 当前值出现一次
}
return ans;
}
int main() {
vector<int> nums = {3, 1, 2};
cout << countInversions(nums) << endl; // 输出2
return 0;
}
对于代码种离散化的解释: