使序列有序的最少交换次数-任意交换元素位置
题意: 给定一个包含1-n的数列,通过交换任意两个元素给数列重新排序。求最少需要多少次交换,能把数组排成按1-n递增的顺序,(数组中的元素互不重复)。
举例: 原数列为2,4,3,6,5,7;第一次交换(3,4)得2,3,4,6,5,7;第二次交换(5,6)得2,3,4,5,6,7。完成排序需2次。
思想: 构造“循环节”,找原位置与排序后位置不一致的形成交换环。元素2位置无需变动自形成一个环;元素4位置需变动(应在元素3位置),因此将元素4与经排序后要放入的位置处元素(‘3’)形成一个环;同理6,5形成一个环;7自形成一个环。一共四个环。我们都知道在一个环内将其元素按有序排列所要的交换次数为:环内元素个数-1。因此求解此题次数n = 数列元素个数-环数。
证明:假设有m个环,完成任务总的交换次数 = 环1元素个数-1+环2元素个数-1+....+环m元素个数-1 = (环1元素个数+环2元素个数+.....+环m元素个数 = 数列元素总个数)- (1+1+....+1 = m)。
代码:
#include<iostream> #include<cstdio> #include<map> #include<algorithm> using namespace std; int a[100], b[100],flag[100]; int main(){ int n, circle = 0; map<int, int> mp; cin >> n; for(int i = 1; i <= n; i++){ scanf("%d",&a[i]); b[i] = a[i]; mp[a[i]] = i; } sort(b+1,b+1+n); for(int j = 1; j <= n; j++){ if(!flag[j]){ int tp = j; while(!flag[tp]){ flag[tp] = 1; tp = mp[b[tp]]; } circle++;//循环节大小 } } cout << n - circle;//最小交换次数 return 0; }
#include<bits/stdc++.h> using namespace std; /** * 交换任意两数的本质是改变了元素位置, * 故建立元素与其目标状态应放置位置的映射关系 */ int getMinSwaps(vector<int> v) { vector<int> v1(v); //将A内元素复制到B。 sort(v1.begin(), v1.end()); map<int,int> m; int len = v.size(); for (int i = 0; i < len; i++) { m[v1[i]] = i; // 建立每个元素与其应放位置的映射关系 } int loops = 0; // 循环节个数 vector<bool> flag(len, false); //初始化 //找出循环节的个数 for (int i = 0; i < len; i++) { if (!flag[i]) { int j = i; while (!flag[j]) //对环处理 { flag[j] = true; j = m[v[j]]; //原序列中j位置的元素在有序序列中的位置 } loops++; } } return len - loops; } vector<int> v; int main() { int n,k; cin>>n; while(n--){ cin>>k; v.push_back(k); } int num = getMinSwaps(v); cout<<"交换次数:"<<num<<endl; return 0; }
然后看例题
现在有两个长度为n的排列p和s。要求通过交换使得p变成s。交换 pipi 和 pjpj 的代价是|i-j|。要求使用最少的代价让p变成s。
输入
单组测试数据。 第一行有一个整数n (1≤n≤200000),表示排列的长度。 第二行有n个范围是1到n的整数,表示排列p。每个整数只出现一次。 第三行有n个范围是1到n的整数,表示排列s。每个整数只出现一次。
输出
输出一个整数,表示从排列p变到s最少要多少代价。
输入样例
样例输入1
4
4 2 1 3
3 2 4 1
输出样例
样例输出1 3
这个题我是看了上面的题解有一个想法,就是如果它形成一个换那么它走的肯定是2*权值
#include<iostream> #include<algorithm> #include<map> using namespace std; typedef long long ll; const int maxn=2e6+100; ll a[maxn],b[maxn],c[maxn],d[maxn],flag[maxn]; ll mp[maxn]; int n; int main(){ cin>>n; ll ans=0; for(int i=1;i<=n;i++){ cin>>c[i]; } for(int i=1;i<=n;i++){ int x; cin>>x; d[x]=i; } for(int i=1;i<=n;i++){ a[i]=d[c[i]]; } for(int i=1;i<=n;i++){ b[i] = a[i]; mp[a[i]] = i; } sort(b+1,b+1+n); for(int j = 1; j <= n; j++){ if(!flag[j]){ int tp = j; ll sum=0; while(!flag[tp]){ flag[tp] = 1; sum+=abs(tp-mp[b[tp]]); tp = mp[b[tp]]; } ans+=sum/2; } } cout<<ans<<endl; }
其实这个题是可以更简单的
#include<set> #include<map> #include<stack> #include<queue> #include<vector> #include<string> #include<math.h> #include<time.h> #include<stdio.h> #include<iostream> #include<string.h> #include<stdlib.h> #include<algorithm> #include<functional> using namespace std; typedef long long ll; #define inf 1000000000 #define mod 1000000007 #define maxn 200005 #define PI 3.1415926 #define lowbit(x) (x&-x) #define eps 1e-9 ll a[maxn]; int main(void) { ll n, i, ans = 0, x; scanf("%lld", &n); for (i = 1;i <= n;i++) { scanf("%lld", &x); a[x] = i; } for (i = 1;i <= n;i++) { scanf("%lld", &x); ans += abs(i - a[x]); } printf("%lld\n", ans / 2); return 0; }

浙公网安备 33010602011771号