您的位置:首页 > 其它

HDU-1394-Minimum Inversion Number

2017-01-11 18:37 375 查看
ACM模版

描述



题解

题意一开始没看懂,懵了许久,而后了解到,一个由 0~n-1 组成的序列,每次都可以把队首的元素移动到队尾,求形成的 n 个序列中最小逆序对数目。

这个问题简化看来就是求逆序数,先求原始状况逆序数,其他可以递推出来,假设初始序列逆序数个数为 N 个,那么将序列首放到序列尾,逆序数会减少 A[i] 个,然后另外增加 N-(A[i]+1) 个,枚举所有状态,获取最优解即可。

所以问题的关键只剩下第一个如何求逆序数,方法有很多,因为 n 比较小,所以可以暴力,高效一些的方法也有,线段树可解,当然,求逆序数比较常用的方法还是要数归并排序求逆序数了,一个模板解决。鉴于最近在做线段树专项训练,所以用线段树解了一下下,结点插入,区间求和~~~

代码

#include <iostream>
#include <cstdio>

using namespace std;

const int MAXN = 5555;

int sum[MAXN << 2];

void pushUp(int root)
{
sum[root] = sum[root << 1] + sum[root << 1 | 1];
}

void build(int root, int l, int r)
{
sum[root] = 0;

if (l == r)
{
return ;
}
int m = (l + r) >> 1;
build(root << 1, l, m);
build(root << 1 | 1, m + 1, r);
}

void update(int root, int val, int l, int r)
{
if (l == r)
{
sum[root]++;
return ;
}

int m = (l + r) >> 1;
if (val <= m)
{
update(root << 1, val, l, m);
}
else
{
update(root << 1 | 1, val, m + 1, r);
}
pushUp(root);

}

int query(int L, int R, int l, int r, int root)
{
if (L <= l && r <= R)
{
return sum[root];
}

int m = (l + r) >> 1;
int ret = 0;
if (L <= m)
{
ret += query(L, R, l, m, root << 1);
}
if (R > m)
{
ret += query(L, R, m + 1, r, root << 1 | 1);
}
return ret;
}

int A[MAXN];

int main(int argc, const char * argv[])
{
int n;

while (~scanf("%d", &n))
{
build(1, 0, n - 1);
int sum = 0;
for (int i = 0; i < n; i++)
{
scanf("%d", A + i);
sum += query(A[i], n - 1, 0, n - 1, 1);
update(1, A[i], 0, n - 1);
}
int ret = sum;
for (int i = 0; i < n; i++)
{
sum += n - A[i] - A[i] - 1;
ret = min(ret, sum);
}

printf("%d\n", ret);
}

return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  线段树 逆序数