HDU 1394 Minimum Inversion Number(最小逆序数/暴力 线段树 树状数组 归并排序)
2016-08-15 18:05
330 查看
题目链接: 传送门
Minimum Inversion Number
Time Limit: 1000MS Memory Limit: 32768 K
Description
The inversion number of a given number sequence a1, a2, ..., an is the number of pairs (ai, aj) that satisfy i < j and ai > aj.
For a given sequence of numbers a1, a2, ..., an, if we move the first m >= 0 numbers to the end of the seqence, we will obtain another sequence. There are totally n such sequences as the following:
a1, a2, ..., an-1, an (where m = 0 - the initial seqence)
a2, a3, ..., an, a1 (where m = 1)
a3, a4, ..., an, a1, a2 (where m = 2)
...
an, a1, a2, ..., an-1 (where m = n-1)
You are asked to write a program to find the minimum inversion number out of the above sequences.
Input
The input consists of a number of test cases. Each case consists of two lines: the first line contains a positive integer n (n <= 5000); the next line contains a permutation of the n integers from 0 to n-1.
Output
For each case, output the minimum inversion number on a single line.
Sample Iutput
10 1 3 6 9 0 8 5 7 4 2
Sample Output
16
解题思路:
1、暴力
注意到输入的n个数是从
0~n-1并且每次都把第一个数移到最后一个数,所以原来比它小的数(和它构成逆序)在移动之后就不是逆序了,而原来比它大的数>(不和它构成逆序)在移动之后就是逆序了,因此很容易推得每次减少的逆序数为
n-1-a[i]每次增加的逆序数为
a[i]
2、线段树
首先先来看一个序列 6 1 2 7 3 4 8 5,此序列的逆序数为5+3+1=9。冒泡法可以直接枚举出逆序数,但是时间复杂度太高O(n^2)。冒泡排序的原理是枚举每一个数组,然后找出这个数后面有多少个数是小于这个数的,小于它逆序数+1。仔细想一下,如果我们不用枚举这个数后面的所有数,而是直接得到小于这个数的个数,那么效率将会大大提高。
总共有N个数,如何判断第i+1个数到最后一个数之间有多少个数小于第i个数呢?不妨假设有一个区间 [1,N],只需要判断区间[i+1,N]之间有多少个数小于第i个数。如果我们把总区间初始化为0,然后把第i个数之前出现过的数都在相应的区间把它的值定为1,那么问题就转换成了[i+1,N]值的总和。再仔细想一下,区间[1,i]的值+区间[i+1,N]的值=区间[1,N]的值(i已经标记为1),所以区间[i+1,N]值的总和等于N-[1,i]的值!因为总共有N个数,不是比它小就是比它(大或等于)。
现在问题已经转化成了区间问题,枚举每个数,然后查询这个数前面的区间值的总和,N-[1,i]即为逆序数。
暴力求解
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; typedef __int64 LL; int main() { int N; while (~scanf("%d",&N)) { int ans[5005] = {0}; int sum = 0,res; for (int i = 0;i < N;i++) { scanf("%d",&ans[i]); } for (int i = 0;i < N;i++) { for (int j = i + 1;j < N;j++) { if (ans[i] > ans[j]) { sum++; } } } res = sum; for (int i = 0;i < N;i++) { sum += N - ans[i] - ans[i] - 1; res = min(res,sum); } printf("%d\n",res); } return 0; }
线段树
#include<cstdio> #include<algorithm> using namespace std; #define lson l , m , rt << 1 #define rson m + 1, r , rt << 1 | 1 const int maxn = 5005; int sum[maxn<<2]; void PushUp(int rt) { sum[rt] = sum[rt<<1] + sum[rt<<1|1]; } void build(int l,int r,int rt) { sum[rt] = 0; if (l == r) return; int m = (l + r) >> 1; build(lson); build(rson); } void upd(int p,int l,int r,int rt) { if (l == r) { sum[rt]++; return; } int m = (l + r) >> 1; if (p <= m) upd(p,lson); else upd(p,rson); PushUp(rt); } int qry(int L,int R,int l,int r,int rt) { if (L <= l && r <= R) { return sum[rt]; } int m = (l + r) >> 1; int ret = 0; if (L <= m) ret += qry(L,R,lson); if (R > m) ret += qry(L,R,rson); return ret; } int main() { int N; while (~scanf("%d",&N)) { int ans[maxn]; build(0,N - 1,1); int sum = 0; for (int i = 0; i < N;i++) { scanf("%d",&ans[i]); sum += qry(ans[i],N - 1,0,N - 1,1); upd(ans[i],0,N - 1,1); } int ret = sum; for (int i = 0;i < N;i++) { sum += N - ans[i] - ans[i] - 1; ret = min(ret,sum); } printf("%d\n",ret); } return 0; }
树状数组
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int maxn = 5005; int c[maxn],N; void upd(int i,int v) { while (i <= N) { c[i] += v; i += i & -i; } } int sum(int i) { int ret = 0; while (i > 0) { ret += c[i]; i -= i & -i; } return ret; } int main() { while (~scanf("%d",&N)) { int ans[maxn] = {0}; int res = 0,tmp; memset(c,0,sizeof(c)); for (int i = 0;i < N;i++) { scanf("%d",&ans[i]); res += sum(N) - sum(ans[i] + 1); upd(ans[i] + 1,1); } tmp = res; for (int i = 0;i < N;i++) { tmp -= ans[i]; tmp += N - ans[i] - 1; res = min(tmp,res); } printf("%d\n",res); } return 0; }
归并排序
#include<iostream> #include<cstdio> #include<cstring> #include<cstdlib> using namespace std; const int maxn = 5005; int sum; void merge_array(int array[],int left,int mid,int right) { if (left >= right) return; int i = left,j = mid + 1,k = 0; int *p; p = (int *)malloc((right - left + 1)*sizeof(int)); while (i <= mid && j <= right) { if (array[i] <= array[j]) { p[k++] = array[i++]; } else { p[k++] = array[j++]; sum += mid - i + 1; //[i-mid]序列就都能与a[j]构成逆序对,故:mid-i+1 } } while (i <= mid) { p[k++] = array[i++]; } while (j <= right) { p[k++] = array[j++]; } for (int i = 0;i < k;i++) { array[i+left] = p[i]; } free(p); } void merge_sort(int array[],int left,int right) { if (left >= right) return; int mid = (left + right)>>1; merge_sort(array,left,mid); merge_sort(array,mid + 1,right); merge_array(array,left,mid,right); } int main() { int N; while (~scanf("%d",&N)) { int a[maxn],b[maxn],res; sum = 0; for (int i = 0;i < N;i++) { scanf("%d",&a[i]); b[i] = a[i]; } merge_sort(a,0,N - 1); res = sum; for (int i = 0;i < N;i++) { sum += N - b[i] - b[i] - 1; res = min(res,sum); } printf("%d\n",res); } return 0; }
相关文章推荐
- hdu1394 Minimum Inversion Number 最小逆序数 线段树单点更新区间查询
- HDU 1394 Minimum Inversion Number 线段树求最小逆序数
- HDU 1394 Minimum Inversion Number 最小逆序数 线段树
- [hdu]1394 Minimum Inversion Number -- 暴力求逆序、树状数组求逆序、线段树求逆序、归并排序求逆序
- hdu 1394 Minimum Inversion Number(最小逆序数) 线段树/暴力
- HDU 1394 Minimum Inversion Number (最小逆序对数&线段树)
- hdu1394 Minimum Inversion Number 线段树求逆序数
- hdu 1394 Minimum Inversion Number(逆序对数+线段树)
- [HDU 1394]Minimum Inversion Number[逆序对][线段树]
- HDU 1394 Minimum Inversion Number(暴力/线段树)
- HDU 1394 Minimum Inversion Number(循环数组求逆序数)(暴力,归并排序,线段树)
- HDU-1394-Minimum Inversion Number(最小逆序数-模拟)
- HDU 1394 Minimum Inversion Number (线段树 求逆序对)
- hdu 1394 Minimum Inversion Number(线段树求最小逆序数)
- HDU 1394 Minimum Inversion Number【线段树求逆序数】
- HDU_1394_Minimum Inversion Number_线段树求逆序数
- hdu 1394 Minimum Inversion Number 线段树求最小逆序数
- 【线段树求逆序数】【HDU1394】Minimum Inversion Number
- [HDU] 1394 Minimum Inversion Number [线段树求逆序数]
- HDU1394 Minimum Inversion Number 【线段树】+【逆序数】