题意:给出长度为n的序列,每次只能交换相邻的两个元素,问至少要交换几次才使得该序列为递增序列。
我们需要知道:逆序数 = 在只允许相邻两个元素交换的条件下,得到有序序列的交换次数
所以我们需要求数列的逆序数,O(N*N)的算法肯定会超时的,
所有我们寻求较为高效的排序方法,归并排序就是充分利用分治法的而提高效率的排序方法。
归并排序:
#include <cstdio>
#define M 500005
int n , s[M];
long long ans;
void move(int l, int mid, int r)
{
int len = r-l+1;
int *a = new int[len+2];
int li = l, ri = mid+1, i = 1;
for(; li<=mid&&ri<=r;)
if(s[li]<=s[ri]) a[i++] = s[li++];
else
{
a[i++] = s[ri++];
ans+=(mid-l+1-li+l);
}
while(li<=mid) a[i++] = s[li++];
while(ri<=r) a[i++] = s[ri++];
for(int j = 1; j <= len; ++j)
s[j+l-1] = a[j];
delete a;
}
void merge(int l, int r)
{
if(l>=r) return;
int mid = l+(r-l)/2;
merge(l,mid);
merge(mid+1,r);
move(l, mid, r);
}
int main ()
{
while(scanf("%d",&n), n)
{
for(int i = 1; i <= n; ++i)
scanf("%d",&s[i]);
ans = 0;
merge(1,n);
printf("%I64d\n",ans);
}
return 0;
}
树状数组:
如果第一次接触树状数组的,可以看参考http://blog.youkuaiyun.com/q573290534/article/details/6664902
离散化+树状数组,====>求逆序数。
思路:把离散化的数组【9 1 0 5 4】==>【 5 2 1 4 3】逆序更新状态即可,
例如
1、查找以3开头的逆序数,把 就是查找已经插入的比3小的所有数字的个数。然后把3插入到数组【0】
2、查找以4开头的逆序数,更新4【1】
3、查找以1开头的逆序数,更新1【0】
4、。。。2。。。。。【1】
5、。。。5。。。。。【4】
最后答案就是【1+1+4 = 6 】
代码如下:
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define M 500005
#define lowbit(x) x&-x
struct Node{
int v, x;
};
Node a[M];
int n, r[M], tree[M];
int comp(const Node p, const Node q) { return p.v<q.v; }
void update(int x)
{
while(x<=n)
{
tree[x]+=1;
x+=lowbit(x);
}
}
int sum(int x)
{
int ret = 0;
while(x>0)
{
ret+=tree[x];
x-=lowbit(x);
}
return ret;
}
int main ()
{
int t;
while(scanf("%d",&n), n)
{
for(int i = 1; i <= n; ++i)
{
scanf("%d",&t);
a[i].v = t;
a[i].x = i;
}
sort(a+1,a+1+n,comp);
for(int i = 1; i <= n; ++i)
r[a[i].x] = i;
long long ans = 0;
memset(tree,0,sizeof(tree));
for(int i = n; i >= 1; i--)
{
ans+=sum(r[i]);
update(r[i]);
}
printf("%I64d\n",ans);
}
return 0;
}