您的位置:首页 > 其它

hdu 1394 Minimum Inversion Number 树状数组求逆序数对(原理)

2015-05-21 15:18 399 查看
好吧,这是作为ACM弱渣的我第一次写博客,值得纪念!!!在acm集训时接触树状数组不久,只能说对它神奇的位运算惊叹不已,但是实际运用到题目,只能惊叹好难运用,做不出来。那么对于hdu1394这题关于求逆序数对,对于大神自然是一道超级水题,但是我也是苦思冥想,为什么可以运用树状数组加速。也许很多新手像我一样,思维限制在了树状数组那些较为复杂的运算中(或许我逻辑能力较差,各位大神勿喷),朦朦胧胧地感觉到是这么算的,但是又没有办法用逻辑想通,在这里,小弟我觉得应该先把树状数组放下,不要考虑它。

假设树状数组为c[i],它所要加速的数组为a[i],输入的为b[i],我们先只考虑a[i]和b[i],朴素的想法是每输入一个b[i],就判断从0<=j<=i-1,如果b[j]>b[i],那么ans++(当然,这道题完全可以这么水过去)。那么现在,每输入一个b[i],我就用a[b[i]+1]++去标记总共输入多少次了(因为输入的数据是0~n-1,所以输入的b[i]就相当于a[i]的下标i,而且有0,树状数组无法对下标为0进行操作,所以要a[b[i]+1]),那么现在很明显了,对于一个b[i],要想查询它前面有多少大于它的,只需将a[b[i]+1]到a
加起来,也就是求一段数组的和,那么树状数组就上场加速了。

现在,总结一下,我觉得作为新手刚刚接触树状数组无须太过于钻研它如何实现还有它的树状结构,我们应该先关注它的功能,那三个函数代码能实现的功能是什么,就像刚刚接触STL,无须关注它如何实现,会用并且用得正确即可,所以对于树状数组,我们做题时只需抽象出如果一个数组是需要求一段区间的和,并且数组的值会随时变化,那么求和就用树状数组,管它的结构呢,随着学习的深入,我相信会越来越了解它,所以无须一开始就钻牛角尖,每个阶段的学习都要要抓住重点。

第一次写博客,写的不好,请大家指导。

题目:hdu 1394

题意:给定一个数组,先求逆序数对,在通过移动,求最小的逆序数对。

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
int c[5005], n, a[5005];
int lowbit(int t)
{
	return t&(-t);
}
int getsum(int pos)
{
	int ans = 0;
	while (pos > 0)
	{
		ans += c[pos];
		pos -= lowbit(pos);
	}
	return ans;
}
void update(int pos, int val)
{
	while (pos <= n)
	{
		c[pos] += val;
		pos += lowbit(pos);
	}
}
int main(void)
{
	while (scanf("%d", &n) != EOF)
	{
		int ans = 0;
		memset(c, 0, sizeof(c));
		for (int i = 0; i < n; i++)
		{
			scanf("%d", &a[i]);
			ans +=getsum(n)- getsum(a[i] + 1);//编号最大是n-1,加一后是n
			update(a[i] + 1, 1);
		}
		int cnt = ans;
		//printf("%d\n", ans);
		for (int i = 0; i < n; i++)
		{
			cnt = cnt + n - 1 - 2 * a[i];
			ans = min(ans, cnt);
		}
		printf("%d\n", ans);
	}
	return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: