版权声明:点个关注(^-^)V https://blog.csdn.net/weixin_41793113/article/details/89225277
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1394
问题描述
给定数字序列a1,a2,...,an的反转数是满足i <j和ai> aj的对(ai,aj)的数量。
对于给定的数字序列a1,a2,...,a,如果我们将第一个m> = 0个数字移动到seqence的末尾,我们将获得另一个序列。总共n个如下序列:
a1,a2,...,an-1,an(其中m = 0 - 初始序列)
a2,a3,...,an,a1(其中m = 1)
a3,a4,...,an,a1,a2(其中m = 2)
...
an,a1,a2,...,an-1(其中m = n-1)
您被要求编写程序从上述序列中找出最小反转数。
输入
输入包含许多测试用例。每个案例由两行组成:第一行包含正整数n(n <= 5000); 下一行包含从0到n-1的n个整数的排列。
产量
对于每种情况,在一条线上输出最小反转数。
样本输入
10
1 3 6 9 0 8 5 7 4 2
样本输出
16
解法1:暴力+数学优化
找出规律,每次将最前面的数移至末尾,大于arr[i]的数为(N-1-arr[i]),小于arr[i]的数为arr[i].
大佬们暴力找逆序对的时候都是点向后找,这里确定是向后找,才容易联想到上面的这个规律
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int a[5005];
int main(){
int n,ans,cnt;
while(~scanf("%d",&n)){
ans=99999999;
cnt=0;
memset(a,0,sizeof(a));
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
for(int j=1;j<i;j++)
if(a[j]>a[i])
cnt++;
}
ans = min(ans,cnt);
for(int i=1;i<=n;i++){
cnt = cnt - a[i] + n - a[i] - 1;
ans = min(ans,cnt);
}
printf("%d\n",ans);
}
return 0;
}
解法2:归并排序
最后的处理循环操作不能对a数组操作,因为它排好序了
#include<iostream>
#include<cstdio>
using namespace std;
int a[5005],b[5005],c[5005];
int n;
int merge(int l,int r) {
int mid = l + (r-l)/2;
int i=l,j=mid+1,k=l,ans=0;
for(int i=l; i<=r; i++)
b[i] = a[i];
while(i<=mid && j<=r) {
if(b[i]>b[j]) {
a[k++] = b[j++];
ans+=mid+1-i;
} else
a[k++] = b[i++];
}
while(i<=mid) {
a[k] = b[i];
k++,i++;
}
while(j<=r) {
a[k] = b[j];
k++,j++;
}
return ans;
}
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);
ans+=merge(l,r);
return ans;
}
int main() {
while(~scanf("%d",&n)) {
for(int i=1; i<=n; i++){
scanf("%d",&a[i]);
c[i] = a[i];
}
int cnt = merge_sort(1,n);
int ans = cnt;
for(int i=1;i<=n;i++){
cnt = cnt - c[i] + n - c[i] - 1;//不能对a数组操作,因为它排好序了
ans = min(ans,cnt);
}
printf("%d\n",ans);
}
return 0;
}
解法3:线段树
从左开始插入a[]数组,用线段树表示区间[l,r]中已经插入的数字总数,每次插入a[i]前,先查找区间[a[i],n-1]中的已插入的总数,这就是a[i]所构成的逆序对,然后再把a[i]插入到相应的下标
#include<iostream>
#include<cstdio>
using namespace std;
const int MAX = 5050;
struct {
int l,r,sum;
}tree[4*MAX];
int n;
int a[MAX];
void buildTree(int u,int l,int r){
tree[u].l = l;
tree[u].r = r;
tree[u].sum = 0;
if(l==r)
return;
int mid = l + (r-l)/2;
buildTree(2*u,l,mid);
buildTree(2*u+1,mid+1,r);
}
int query(int u,int x,int y){
if(x<=tree[u].l && y>=tree[u].r)
return tree[u].sum;
int mid = tree[u].l + (tree[u].r-tree[u].l)/2;
if(y<=mid)
return query(2*u,x,y);
else if(x>mid)
return query(2*u+1,x,y);
else
return query(2*u,x,mid)+query(2*u+1,mid+1,y);
}
void update(int u,int x){
if(tree[u].l==x && tree[u].r==x){
tree[u].sum=1;
return;
}
int mid = tree[u].l + (tree[u].r-tree[u].l)/2;
if(x<=mid)
update(2*u,x);
else if(x>mid)
update(2*u+1,x);
tree[u].sum = tree[2*u].sum + tree[2*u+1].sum;
}
int main(){
while(~scanf("%d",&n)){
buildTree(1,0,n-1);//tree[]表示0~n-1区间有多少数
int cnt = 0;
for(int i=0;i<n;i++){//a数组下标从1或者0开始都可以,看你喜欢
scanf("%d",&a[i]);
cnt+=query(1,a[i],n-1);
update(1,a[i]);
}
int ans = cnt;
for(int i=0;i<n;i++){
cnt = cnt - a[i] + n - a[i] - 1;
ans = min(ans,cnt);
}
printf("%d\n",ans);
}
return 0;
}