我们部门要排队唱歌,大家乱哄哄的挤在一起,现在需要按从低到高的顺序拍成一列,但每次只能交换相邻的两位,请问最少要交换多少次
即求逆序对,采用归并排序思想,参考https://blog.csdn.net/Fan0628/article/details/88965141
import java.util.*; public class Main { public static void main(String[] args) { Scanner scanner = new Scanner(System.in); int n = scanner.nextInt(); int[] height = new int[n]; for (int i = 0; i < n; i++) { height[i] = scanner.nextInt(); } // int count = 0; // for (int i = 0; i < n; i++) { // for (int j = 0; j < i; j++) { // if (height[j] > height[i]) { // count++; // } // } // } // System.out.println(count); System.out.println(inversePairs(height)); } private static int inversePairs(int[] arr) { int[] copy = new int[arr.length]; for (int i = 0; i < arr.length; i++) { copy[i] = arr[i]; } int count = inversePairsCore(arr, copy, 0, arr.length - 1); return count; } private static int inversePairsCore(int[] arr, int[] copy, int head, int tail) { if (head == tail) { return 0; } // mid的位置 int mid = (head + tail) >> 1; // 递归,分别计算左边和右边内部的逆序对个数 int leftCount = inversePairsCore(arr, copy, head, mid); int rightCount = inversePairsCore(arr, copy, mid + 1, tail); // count计数左右两者之间的对数 int count = 0; // i指向mid,j指向tail int i = mid; int j = tail; // 临时数组的指针,指向tail int tempCopy = tail; while (i >= head && j > mid) { // 如果arr[i]大于arr[j] if (arr[i] > arr[j]) { // i指向的数大于右半部分所有的数,计数加入j-mid count += j - mid; // 临时数组存放i指向的数,同时tempCopy和i都向前移动 copy[tempCopy--] = arr[i--]; // 如果arr[i]小于arr[j],tempCopy和j都向前移动 } else { copy[tempCopy--] = arr[j--]; } } // 移动完之后将没存放的数字存入临时数组,例如[1,2,3,4][5,6,7,8], // 临时数组存完后面四个[5,6,7,8],j移动到mid移动终止,将剩下的[1,2,3,4]存入临时数组 for (; i >= head; i--) { copy[tempCopy--] = arr[i]; } // 同理,这也是移动完之后将没存放的数字存入临时数组,例如[5,6,7,8][1,2,3,4], // 临时数组存完后面四个[5,6,7,8],i移动到head移动终止,将剩下的[1,2,3,4]存入临时数组 for (; j > mid; j--) { copy[tempCopy--] = arr[j]; } // 将临时数组复制到原数组,以便递归 System.arraycopy(copy, head, arr, head, tail - head + 1); return (leftCount + rightCount + count); } }
用归并排序的思想可以降到 n*logn
import java.util.Scanner; import static java.lang.System.in; public class Main { public static void main(String[] args) { Scanner sc = new Scanner(in); int n = sc.nextInt(); int[] data = new int[n]; for (int i = 0; i < n; i++) { data[i] = sc.nextInt(); } temp = new int[n]; mergeWay(data, 0, n - 1); System.out.println(count); } public static void mergeWay(int[] data, int begin, int end) { if (begin >= end) { return; } int mid = begin + ((end - begin) >> 1); mergeWay(data,begin,mid); mergeWay(data,mid+1,end); merge(data, begin, end, mid); } public static int[] temp; public static int count = 0; private static void merge(int[] data, int begin, int end, int mid) { int i = begin, j = mid + 1; int k = i; while (i <= mid && j <= end) { count += data[i] <= data[j] ? 0 : mid - i + 1;//这里加的逆序对是这么多,而不是加1 temp[k++] = data[i] <= data[j] ? data[i++] : data[j++]; //先写上面,这里i,j已变化 } while (i <= mid) { temp[k++] = data[i++]; } while (j <= end) { temp[k++] = data[j++]; } for (int h = begin; h < k; h++) { data[h] = temp[h]; } } }
N = int(input()) s = [] c = 0 s = [int(input()) for i in range(N)] for i in range(N): c = c + s.index(min(s)) s.remove(min(s)) print(c)1、找最小值,移到首位,去掉这个最小值,
#include <iostream> #include <algorithm> using namespace std; int heights[50000]; int main(int argc, char* argv[]){ int N; cin >> N; for(int idx = 0; idx < N; idx++) cin >> heights[idx]; int step = 0; for(int idx = 1; idx < N; idx++){ int pos = upper_bound(heights, heights+idx, heights[idx]) - heights; if(pos < idx){ step += idx - pos; int tmp = heights[idx]; for(int i = idx; i > pos; i--) heights[i] = heights[i-1]; heights[pos] = tmp; } } cout << step << endl; return 0; }
#include<iostream> #include<vector> using namespace std; int main(){ int i,j,n,tmp,res=0; cin>>n; vector<int> people(n); for(i=0;i<n;i++) cin>>people[i]; for(i=n-1;i>=0;i--){ for(j=0;j<i;j++){ if(people[j] > people[j+1]){ tmp = people[j]; people[j] = people[j+1]; people[j+1] = tmp; res++; } } } cout<<res<<endl; return 0; }
N = int(input()) H = [] for i in range(N): H.append(int(input())) def func(h): ''' :param h: :return: 每次取第一个数,然后对之后的数进行比较,当比他小时,就插入到前面,否则不动 完成这个操作后,比它小的数都在左边,大的数都在右边,且该数也在相应的位置上 然后在对左边和右边进行同样的操作 ''' if len(h) == 1&nbs***bsp;len(h) == 0: return 0 temp = h[0] j = 0 count = 0 for i in range(1, len(h)): if h[i] < temp: h.insert(j, h.pop(i)) count += i - j j += 1 count = count + func(h[:j]) + func(h[j + 1:]) return count print(func(H))快排思想
//#include "utils.cpp" #include <bits/stdc++.h> using namespace std; //freopen("temp.in","r",stdin); #include <bits/stdc++.h> using namespace std; /* 归并排序求逆序对个数 对[l,r]进行归并排序 1.划分为两半 2.分别对两部分进行排序 3.合并结果 */ vector<int> A,sorted_A; int merge_sort(int l,int r){ if(l==r) return 0; int ans = 0; int mid = l+(r-l)/2; //划分、排序 ans+=merge_sort(l,mid); ans+=merge_sort(mid+1,r); //合并 int i_l = l,i_r = mid+1; int end_l = mid,end_r = r; int i = l; while(i_l<=end_l and i_r<=end_r){ if(A[i_l]<=A[i_r]){ //左边的元素小时 sorted_A[i++]=A[i_l++]; } else{ sorted_A[i++]=A[i_r++]; //逆序 当前右边i_r的数与 左边i_l以及它右边所有的数都会逆序 ans+=(end_l-i_l+1); } } //剩余的 while(i_l<=end_l) sorted_A[i++]=A[i_l++]; while(i_r<=end_r) sorted_A[i++]=A[i_r++]; i=l; for(;i<=r;i++) A[i]=sorted_A[i]; return ans; } int main(){ //freopen("temp.in","r",stdin); int N; cin>>N; int a; A.resize(N); sorted_A.resize(N); for(int i=0;i<N;i++){ cin>>a; A[i]=a; sorted_A[i]=a; } int ans = merge_sort(0,N-1); cout<<ans<<endl; return 0; }
using System; using System.Collections.Generic; using System.Linq; using System.Text.RegularExpressions; //总结目前牛客问题 第一,没有循环输入问题, 第二 有循环输入问题, 第三 输入有多余空格问题 ,第四 中间插入多余空行问题 .... namespace Test0001 { class Program { public static void Main(string[] args) { string line; while (!string.IsNullOrEmpty(line = Console.ReadLine())) Func(line); //Func(Console.ReadLine()); } public static void Func(string line) { int n = int.Parse(line); SortedList<int, int> res = new SortedList<int, int>(); int count = 0; for (int i = 0; i < n; i++) { int num = int.Parse(Console.ReadLine()); if (!res.ContainsKey(num)) res.Add(num, 0); else res[num]++; count += res.Count - res.IndexOfKey(num) + res[num] - 1; } Console.WriteLine(count); } } } 者这题要是用SortList来解决问题比较简单, 思路,由于SortList是有序的,所以无需对他进行任何排序操作,每次只需要找到该数据出现的索引然后用长度减去索引即为他所需要移动的次数,这里需要注意的是,由于可能出现重复数据,所以在有重复数据插入时res[num]++,(相当于出现次数) 计算长度时减去即可
import java.util.Scanner; /*第一行是N(N<50000),表示有N个人 *然后每一行是人的身高Hi(Hi<2000000,不要怀疑,我们以微米计数),持续N行,表示现在排列的队伍 *输出描述: *输出一个数,代表交换次数。*/ //51 public class Main { static int[] pi; public static void main(String[] args) { Scanner sc = new Scanner(System.in); int n = sc.nextInt(); int[] di = new int[n]; for(int i=0;i<n;i++){ di[i] = sc.nextInt(); } //复杂度太高,采用归并来计算逆序对个数 /*int res = 0; for(int i=0;i<n-1;i++){ for(int j=0;j<n-i-1;j++){ if(di[j]>=di[j+1]){ int temp = di[j]; di[j] = di[j+1]; di[j+1] = temp; res++; } } } System.out.println(res);*/ pi = new int[n]; System.arraycopy(di, 0, pi, 0, n); System.out.println(toFind(di, pi, 0, n-1)); } static int toFind(int[] arr,int[] copy,int L,int R){ if(L==R){ return 0; } int mid = (R+L)/2; int lCount = toFind(arr,copy,L,mid); int rCount = toFind(arr, copy, mid+1, R); int sum = 0; //已分别有序 int q = mid; int p = R; int temp = R; while(q>=L && p>mid){ if(arr[q]>arr[p]){ //有逆序,计算逆序对 sum += p - mid; copy[temp--] = arr[q--]; }else{ copy[temp--] = arr[p--]; } } for (; q >= L; q--) { copy[temp--] = arr[q]; } for (; p > mid; p--) { copy[temp--] = arr[p]; } System.arraycopy(copy, L, arr, L, R-L+1); return lCount+rCount+sum; } }
n = int(input()) list1 = [] for i in range(n): list1.append(int(input())) def InversionNum(lst): # 改写归并排序,在归并排序中,每当R部分元素先于L部分元素插入原列表时,逆序对数要加L剩余元素数 if len(lst) == 1: return lst,0 else: n = len(lst) // 2 lst1,count1 = InversionNum(lst[0:n]) lst2,count2 = InversionNum(lst[n:len(lst)]) lst,count = Count(lst1,lst2,0) return lst,count1+count2+count def Count(lst1, lst2,count): i = 0 j = 0 res = [] while i < len(lst1) and j < len(lst2): if lst1[i] <= lst2[j]: res.append(lst1[i]) i += 1 else: res.append(lst2[j]) count += len(lst1)-i # 当右半部分的元素先于左半部分元素进入有序列表时,逆序对数量增加左半部分剩余的元素数 j += 1 res += lst1[i:] res += lst2[j:] return res,count print(InversionNum(list1)[1])
//插入排序 #include<iostream> #include<vector> using namespace std; int main() { int num; int sum=0; while(cin>>num) { vector<int>vec; while(num--) { int p; cin>>p; vec.push_back(p); } for(int i=1;i<vec.size();i++) { int tmp=vec[i]; int j; for(j=i-1;j>=0;j--) { if(vec[j]>tmp) { vec[j+1]=vec[j]; sum++; } else break; } vec[j+1]=tmp; } } cout<<sum<<endl; return 0; }