题目链接: 传送门

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;
}

HDU 1394 Minimum Inversion Number(最小逆序数/暴力 线段树 树状数组 归并排序)的更多相关文章

  1. HDU 1394 Minimum Inversion Number(最小逆序数 线段树)

    Minimum Inversion Number [题目链接]Minimum Inversion Number [题目类型]最小逆序数 线段树 &题意: 求一个数列经过n次变换得到的数列其中的 ...

  2. HDU 1394.Minimum Inversion Number-最小逆序数-完全版线段树(单点增减、区间求和)

    HDU1394.Minimum Inversion Number 这个题求最小逆序数,先建一个空的树,然后每输入一个值,就先查询一下,查询之后,更新线段树,然后遍历一遍,每次将第一个数放到最后之后,减 ...

  3. hdu1394 Minimum Inversion Number(最小逆序数)

    Minimum Inversion Number Time Limit : 2000/1000ms (Java/Other)   Memory Limit : 65536/32768K (Java/O ...

  4. HDU 1394 Minimum Inversion Number(线段树求最小逆序数对)

    HDU 1394 Minimum Inversion Number(线段树求最小逆序数对) ACM 题目地址:HDU 1394 Minimum Inversion Number 题意:  给一个序列由 ...

  5. HDU.1394 Minimum Inversion Number (线段树 单点更新 区间求和 逆序对)

    HDU.1394 Minimum Inversion Number (线段树 单点更新 区间求和 逆序对) 题意分析 给出n个数的序列,a1,a2,a3--an,ai∈[0,n-1],求环序列中逆序对 ...

  6. HDU 1394 Minimum Inversion Number ( 树状数组求逆序数 )

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1394 Minimum Inversion Number                         ...

  7. hdu 1394 Minimum Inversion Number 逆序数/树状数组

    Minimum Inversion Number Time Limit: 1 Sec  Memory Limit: 256 MB 题目连接 http://acm.hdu.edu.cn/showprob ...

  8. HDU 1394 Minimum Inversion Number(线段树/树状数组求逆序数)

    Minimum Inversion Number Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java ...

  9. hdu 1394 Minimum Inversion Number(逆序数对) : 树状数组 O(nlogn)

    http://acm.hdu.edu.cn/showproblem.php?pid=1394  //hdu 题目   Problem Description The inversion number ...

随机推荐

  1. Win7系统.net framework 4.0没有注册导致部署在IIS的站点跑不起来怎么办

    win7装了VS再装IIS,结果IIS里面有.NET4.0,但是程序始终是跑不起来,怎么办呢? 分析觉得可能是因为4.0没有注册到IIS,在win7下如果先安装vs2010 (附带会安装Microso ...

  2. 马化腾:办公用QQ休闲用微信[Dream Catchers论坛]

    近日,香港大学举办以创新创业为主题的Dream Catchers论坛.其中腾讯董事局主席马化腾在下午两点四十五分在李兆基会议中心做了专题演讲,分享了自己的创业经历并回答了媒体人张力奋有关产品.整整对手 ...

  3. Cup(二分)

    题目连接:http://acm.hdu.edu.cn/showproblem.php?pid=2289 hdu_2289:Cup Time Limit: 3000/1000 MS (Java/Othe ...

  4. Codeforces475D - CGCDSSQ

    Portal Description 给出长度为\(n(n\leq10^5)\)的序列\(\{a_n\}\),给出\(q(q\leq3\times10^5)\)个\(x\),对于每个\(x\),求满足 ...

  5. 第五周博客作业 &lt;西北师范大学| 周安伟&gt;

    第五周博客作业 一,助教博客链接https://home.cnblogs.com/u/zaw-315/ 二,本周工作查阅项目汇报,班级微信群.对同学们的中期项目汇报进行查看,解决上周留言问题,对及时出 ...

  6. 为你的VPS进行一些安全设置吧

    安全是一个VPS最基本的必备条件,若您的VPS三天两头被人攻破,那么对于网站来说也没什么意义了,所以,在创建了Web服务器之后,您首先要做的事情就是将您的VPS加固,至少让普通黑客没有办法能够攻破您的 ...

  7. [Erlang28]使用匿名函数灵活组合不同的case

    cowboy_http.erl里面的date1/2 启示: 以前一般写case里都是这样子: date1(Date) -> case month1(Date) of {error,badarg} ...

  8. 使用VNC访问Windows桌面

    1. 背景介绍 两台电脑,一个笔记本,一个台式机 笔记本上装的是Windows 10, 通过上网小助手上网 (P.S.  上网小助手...Stupid Policy...) 台式机上装的是Ubuntu ...

  9. javascript判断一个元素是另一个元素的子元素

    function isParent (obj,parentObj){ while (obj != undefined && obj != null && obj.tag ...

  10. apache POI 操作excel&lt;导入导出&gt;

    1.首先导入maven依赖 <!-- POI核心依赖 --> <dependency> <groupId>org.apache.poi</groupId> ...