歸併排序求逆序數(POJ 1804,POJ 2299,HDU 4911)
首先,明確兩個概念:
逆序對:數列a[1],a[2],a[3]…中的任意兩個數a[i],a[j] (i<j),如果a[i]>a[j],那麼我們就說這兩個數構成了一個逆序對.
逆序數:一個數列中逆序對的總數.
例題一:POJ 1804. 點選開啟連結
解題思路:每次交換隻能減少一個逆序,而且必定能減少一個逆序,從而問題就轉換為求逆序個數了。這題資料規模很小,暴力可過。
我這裡提供了用Merge_sort的方法求解逆序數,時間複雜度為O(nlogn).
關於歸併排序:歸併排序是將數列a[l,r]分成兩半a[l,mid]和a[mid+1,r]分別進行歸併排序,然後再將這兩半合併起來。
在合併的過程中(設l<=i<=mid,mid+1<=j<=r),當a[i]<=a[j]時,並不產生逆序數;當a[i]>a[j]時,在前半部分中比a[i]大的數都比a[j]大,將a[j]放在a[i]前面的話,逆序數要加上mid+1-i。因此,可以在歸併排序中的合併過程中計算逆序數。
#include<cstdio> #define N 1000+10 int ans=0; int f[N],t[N]; void Merge(int l,int m,int r) //左右兩個表合併成一個表 { int i=l,j=m+1,cnt=0; while(i<=m && j<=r) { if(f[i]<=f[j]) t[cnt++]=f[i++]; else { ans+=m-i+1; t[cnt++]=f[j++]; //核心程式碼,求解逆序數個數。 } } while(i<=m) //若左表不空 t[cnt++]=f[i++]; while(j<=r) //若右表不空 t[cnt++]=f[j++]; for(int k=0;k<cnt;) //修改原陣列 f[l++]=t[k++]; } void Merge_sort(int l,int r) //歸併排序 { if(l==r) return ; else { int m=(l+r)>>1; Merge_sort(l,m); Merge_sort(m+1,r); Merge(l,m,r); } } int main() { int T,cas=0; scanf("%d",&T); while(T--) { int n; scanf("%d",&n); for(int i=0;i<n;i++) scanf("%d",&f[i]); ans=0; Merge_sort(0,n-1); printf("Scenario #%d:\n%d\n\n",++cas,ans); } return 0; }
例題2:POJ 2299. 點選開啟連結
跟上題沒有什麼區別,但n較大,O(n^2)的演算法必然超時,故必須使用Merge_sort,同時注意要使用 long long 。
#include<cstdio> #define N 500000+10 long long ans=0; int f[N],t[N]; void Merge(int l,int m,int r) { int i=l,j=m+1,cnt=0; while(i<=m && j<=r) { if(f[i]<=f[j]) t[cnt++]=f[i++]; else { ans+=m-i+1; t[cnt++]=f[j++]; } } while(i<=m) t[cnt++]=f[i++]; while(j<=r) t[cnt++]=f[j++]; for(int k=0;k<cnt;) f[l++]=t[k++]; } void Merge_sort(int l,int r) { if(l==r) return ; else { int m=(l+r)>>1; Merge_sort(l,m); Merge_sort(m+1,r); Merge(l,m,r); } } int main() { int n; while(scanf("%d",&n),n) { for(int i=0;i<n;i++) scanf("%d",&f[i]); ans=0; Merge_sort(0,n-1); printf("%lld\n",ans); } return 0; }
例題3: hdu 4911. 點選開啟連結
這裡題目另設條件,只能交換k次,所以,先用Merge_sort求出ans,然後與k進行比較即可(ans<=k ->0 ; ans>k -> ans-k ;)。
#include<cstdio>
#define N 500000+10
long long ans=0;
int f[N],t[N];
void Merge(int l,int m,int r)
{
int i=l,j=m+1,cnt=0;
while(i<=m && j<=r)
{
if(f[i]<=f[j])
t[cnt++]=f[i++];
else
{
ans+=m-i+1;
t[cnt++]=f[j++];
}
}
while(i<=m)
t[cnt++]=f[i++];
while(j<=r)
t[cnt++]=f[j++];
for(int k=0;k<cnt;)
f[l++]=t[k++];
}
void Merge_sort(int l,int r)
{
if(l==r)
return ;
else
{
int m=(l+r)>>1;
Merge_sort(l,m);
Merge_sort(m+1,r);
Merge(l,m,r);
}
}
int main()
{
int n,k;
while(scanf("%d%d",&n,&k)==2)
{
for(int i=0;i<n;i++)
scanf("%d",&f[i]);
ans=0;
Merge_sort(0,n-1);
if(ans>k) printf("%lld\n",ans-k);
else printf("0\n");
}
return 0;
}
相關推薦
歸併排序求逆序數(POJ 1804,POJ 2299,HDU 4911)
首先,明確兩個概念: 逆序對:數列a[1],a[2],a[3]…中的任意兩個數a[i],a[j] (i<j),如果a[i]>a[j],那麼我們就說這兩個數構成了一個逆序對. 逆序數:一個數列中逆序對的總數. 例題一:POJ 1804. 點選開啟連結 解題思
歸併排序求逆序數(模版)
#include<iostream> #include<cstdio> #include<cmath> #include<algorithm> #include<map> #include<cstring> #define ll
歸併排序求逆序數(排序演算法)
歸併排序:歸併排序是建立在歸併操作上的一種有效的排序演算法,該演算法是採用分治法(Divide and Conquer)的一個非常典型的應用。將已有序的子序列合併,得到完全有序的序列;即先使每個子序列有序,再使子序列段間有序。若將兩個有序表合併成一個有序表,稱為
【資料結構排序】POJ1804——歸併排序求逆序數
問題描述: 給定一個數組,問最少經過多少次交換,才可以使得它有序 求解方法: 實際上就是求該陣列的逆序數,使用歸併排序即可 AC程式碼如下: #include<cstdio> #in
利用歸併排序求逆序數
假設A[1…n]是一個有n個不同元素的陣列,若i < j 且 A[i] > A[j],則對偶(i, j)稱為A的一個逆序對。例如,對於陣列[2, 3, 8, 6, 1],它的所有逆序對為(1, 5),(2, 5),(3, 4),(3, 5),(4
排序演算法之歸併排序及利用歸併排序求逆序數
排序演算法之歸併排序 1. 自頂向下的歸併排序 中心思想 將待排序的陣列平均切分為兩半,將前半部分和後半部分分別進行排序,再講兩個有序陣列歸併到一個數組中 特點 遞迴,需要額外的空間(輔助陣列)來儲存切分完成的子陣列,主要難點在於合併
資料結構實驗之排序五:歸併求逆序數(SDUT 3402)
歸併排序詳解(戳我)。 以下是搬了別人的。 #include<stdio.h> #include<stdlib.h> long long sum = 0; int a[100005]; int temp[100005]; void Merge(int s1
ACM_小明滾出去?(歸並排序求逆序數)
思路 name ont ans void long 輸出 lld sort 小明滾出去? Time Limit: 2000/1000ms (Java/Others) Problem Description: 老師:“小明,寫一個排序算法”; 小明: void mys
ACM ICPC 2011–2012, NEERC, Northern Subregional Contest J. John’s Inversions(合併排序求逆序數對數)
題目連結:http://codeforces.com/gym/100609/attachments 題目大意:有n張牌,每張牌有紅色和藍色兩面,兩面分別寫了一些數字,同種顏色的任意兩個數字若排在前面的
hdu 4911 Inversion(歸併排序求逆序對數)2014多校訓練第5場
Inversion Time Limit: 2000/1000 MS (Java/Others) Memory L
【模板】歸併排序求逆序對
歸併排序求逆序對 1 #include <iostream> 2 #include <algorithm> 3 #include <cstring> 4 #include <cstdio> 5 6 typedef long lon
洛谷P1908歸併排序求逆序對
#include<bits/stdc++.h> #define ll long long #define INF 0x3f3f3f3f using namespace std; int n,a[500010],c[500010]; ll ans=0; void msort(int b
歸併排序(求逆序對)
#include<bits/stdc++.h> using namespace std; void merge(int a[],int first,int mid,int last,int temp[]) { int i=first,j=mid+1; in
二分歸併 排序 求逆序對
題目:The Number of Inversions 題面: For a given sequence , the number of pairs where and , is called the number of inv
分治求逆序數(CDQ)
歸併排序 #include<bits/stdc++.h> using namespace std; int b[100]; void merge_sort(int l,int r,int *a){ if(l==r)return; int m=(l+r)>>1; mer
HDU 2689 Sort it 歸併排序求逆序對
題目連結:http://acm.hdu.edu.cn/showproblem.php?pid=2689 思路:就是歸併排序啦。在歸併排序的同時,記錄逆序對就行了。時間複雜度即為歸併排序的複雜度:O(N log N)。 程式碼: #include<iostream>
利用歸併排序求逆序對
在逆序對的問題中,如果採用暴力求解的方法,一般也是有效的,但是O(n2)時間複雜度實在是難以接受的。但是對於逆序對問題,卻有一個看似不想關的演算法來解決–歸併排序。時間複雜度和空間複雜度完全與歸併排序一樣,只是在歸併過程中,添加了一個變數,對於逆序對的數目進行了
hdu1394(暴力/線段樹/歸併排序求逆序對的個數)
題目大意:給出一個序列a1, a2, ..., an-1, an (大小為0<=ai-1<=n-1), 如下所示, a1, a2, ..., an-1, an a2, a3, ..., an, a1 a3, a4, ..., an, a1, a2 ...
poj1804 歸併排序求逆序對
#include<iostream> #include<cstdio> #include<cstdlib> using namespace std; const in
演算法導論2.4 合併排序求逆序數
2-4 逆序對 設A[1...n]是一個包含n個不同數的陣列。如果在i<j的情況下,有A[i]>A[j],則(i,j)就稱為A中的一個逆序對。 (1)列出陣列{2,3,8,6,1}的五個逆序。 (2)如果陣列的元素取自{1,2...,n}