HDU_1394 Minimum Inversion Number(线段树)
2015-08-23 10:50
603 查看
题目请点我
题解:
这道题不容易看出是线段树,但是我们可以先来分析题意。
给出一个数列,注意是a permutation of the n integers from 0 to n-1,其实是其他的算法也是一样的。要找到逆序数最起码是O(N^2)的时间复杂度,之后还要求变换后的。那么对于每一个数来说我们都要进行一次查询,怎样优化查询的时间复杂度呢?对了,线段树!(当然树状数组貌似也可以求解,之后学到了再去整理)线段树作为一个数据结构就是用来维护算法时间空间复杂度的。线段树的写法有很多种,有的用结构体定义节点,函数就可以少写几个形参,不过我比较喜欢用数组定义线段树,形参多写几个,这个看个人习惯,效果是一样的。
对于每次插入一个数num[i],我们可以查询当前集合中已存在的>num[i]的数字,所有的加起来就是逆序数。而查询对于线段树来说就只有O(LogN)的时间复杂度,总的时间复杂度O(NlogN)。
之后是按规定重新排列,对于每一个位于队首的元素a我们可以证明的是,变换后的逆序数sum = sum+(N-1-a)-a;
其中(N-1-a)是比a大的数,现在要加上,算入逆序数,而a表示比a小的数,现在要从原来算入的逆序数中剔除。最终找到逆序数最少有多少个。
代码实现:
题解:
这道题不容易看出是线段树,但是我们可以先来分析题意。
给出一个数列,注意是a permutation of the n integers from 0 to n-1,其实是其他的算法也是一样的。要找到逆序数最起码是O(N^2)的时间复杂度,之后还要求变换后的。那么对于每一个数来说我们都要进行一次查询,怎样优化查询的时间复杂度呢?对了,线段树!(当然树状数组貌似也可以求解,之后学到了再去整理)线段树作为一个数据结构就是用来维护算法时间空间复杂度的。线段树的写法有很多种,有的用结构体定义节点,函数就可以少写几个形参,不过我比较喜欢用数组定义线段树,形参多写几个,这个看个人习惯,效果是一样的。
对于每次插入一个数num[i],我们可以查询当前集合中已存在的>num[i]的数字,所有的加起来就是逆序数。而查询对于线段树来说就只有O(LogN)的时间复杂度,总的时间复杂度O(NlogN)。
之后是按规定重新排列,对于每一个位于队首的元素a我们可以证明的是,变换后的逆序数sum = sum+(N-1-a)-a;
其中(N-1-a)是比a大的数,现在要加上,算入逆序数,而a表示比a小的数,现在要从原来算入的逆序数中剔除。最终找到逆序数最少有多少个。
代码实现:
#include <iostream> #include <cstdio> #include <cstring> #include <cstdlib> #define MAX 5010 using namespace std; int N; int sum; int num[MAX]; int segTree[MAX<<2|1]; void pushUp(int root); void build(int root,int l,int r); void update(int pos,int root,int l,int r); int query(int a,int b,int l,int r,int root); int main(){ while( scanf("%d",&N) != EOF ){ sum = 0; //因为初始建立一棵空树,所以两者作用是一样的 //build(1,1,N); memset(segTree,0,sizeof(segTree)); for( int i = 0; i < N; i++ ){ scanf("%d",&num[i]); //查找现存的比num[i]+1大的数 sum += query(num[i]+1,N,1,N,1); //更新num[i]+1 update(num[i]+1,1,1,N); } int ans = sum; //尝试将a[0]~a[N-2]放到最后,找到最小逆序数 for( int i = 0; i < N-1; i++ ){ sum = sum+N-2*num[i]-1; if( sum < ans ){ ans = sum; } } printf("%d\n",ans); } return 0; } void build(int root,int l,int r){ //初始化为0 if( l == r ){ segTree[root] = 0; return ; } int mid = (l+r)>>1; build(root<<1,l,mid); build(root<<1|1,mid+1,r); pushUp(root); return ; } //合点 void pushUp(int root){ segTree[root] = segTree[root<<1]+segTree[root<<1|1]; return ; } void update(int pos,int root,int l,int r){ if( l == r ){ segTree[root] = 1; return ; } int mid = (l+r)>>1; if( pos <= mid ){ update(pos,root<<1,l,mid); } else{ update(pos,root<<1|1,mid+1,r); } pushUp(root); return ; } int query(int a,int b,int l,int r,int root){ if( a > r || b < l ){ return 0; } if( a<=l && b >= r ){ return segTree[root]; } int mid = (l+r)>>1; return query(a,b,l,mid,root<<1)+query(a,b,mid+1,r,root<<1|1); }
相关文章推荐
- Lua教程(七):数据结构详解
- 解析从源码分析常见的基于Array的数据结构动态扩容机制的详解
- C#数据结构揭秘一
- 数据结构之Treap详解
- JavaScript数据结构和算法之图和图算法
- Java数据结构及算法实例:冒泡排序 Bubble Sort
- Java数据结构及算法实例:插入排序 Insertion Sort
- Java数据结构及算法实例:考拉兹猜想 Collatz Conjecture
- java数据结构之java实现栈
- java数据结构之实现双向链表的示例
- Java数据结构及算法实例:选择排序 Selection Sort
- Java数据结构及算法实例:朴素字符匹配 Brute Force
- Java数据结构及算法实例:汉诺塔问题 Hanoi
- Java数据结构及算法实例:快速计算二进制数中1的个数(Fast Bit Counting)
- java数据结构和算法学习之汉诺塔示例
- Java数据结构及算法实例:三角数字
- Java数据结构之简单链表的定义与实现方法示例
- 数据结构之AVL树详解
- qqwry.dat的数据结构图文解释第1/2页
- JavaScript中数据结构与算法(五):经典KMP算法