hdu 1394 解题报告 简单易懂

Minimum Inversion Number

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 24293    Accepted Submission(s): 14387


 

Problem 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 Input

 

10 1 3 6 9 0 8 5 7 4 2

 

Sample Output

 

16

 

Author

CHEN, Gaoli

 

Source

ZOJ Monthly, January 2003

 

Recommend

Ignatius.L


先贴上别人解这道题的三种解法:https://blog.csdn.net/hnust_xiehonghao/article/details/8988809

首先得了解思路,只有知道了思路才有耐心看代码

这题看了我好久,百度了好多,因为根本就不知道思路,连暴力都看不懂,就是因为下面这个不懂

每次将新的序列的第一个y[i]移到最后,sum将减少a[i]个,当然同时还会增加n-1-a[i];

(a[i] 存的就是输入的数,以案例为例就是 1 3 6 9 0 8 5 7 4 2)

之后才发现它的输入的数是0,1,2,3,..,n-1这n个数(看来我读题不够仔细啊),这样就讲得通下面这个

把a[i]放到末尾时,比a[i]小的数的个数(low(a[i])  就是a[i] ,比a[i]大的数的个数为n-1-a[i]

知道了思路就容易了,先贴上暴力代码(来自上面的链接)

 

#include<stdio.h>
int a[5555];
int main()
{
    int n,i,j,ans=999999999;
    while(scanf("%d",&n)!=EOF)
    {
        ans=999999999;
           for(i=0;i<n;i++) scanf("%d",&a[i]);
           int cnt=0;
           for(i=0;i<n;i++)
               for(j=i+1;j<n;j++)
               {
                   if(a[i]>a[j]) cnt++;
               }
           // printf("cnt=%d\n",cnt);
           if(ans>cnt)  ans=cnt;
           for(i=0;i<n;i++)
           {
               cnt=cnt-a[i]+n-1-a[i];
               if(ans>cnt)  ans=cnt;
            }
           printf("%d\n",ans);
    }
    return 0;
}

对于线段树的代码可以自己根据这个主要思路打出代码

结合这个方法

下面的链接中我将线段树代码部分贴了出来,链接中的该部分有些小小滴错误

在链接中的注释的基础上,添加了一点自己理解的注释,可以先看看每个函数的作用(注释部分)


#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <map>

using namespace std;
#define ll long long
const int maxn=5005;
struct Tree
{
    int left,right;
    int val;
}tree[maxn<<2];
int  val[maxn];

void build(int l,int r,int root)
{
    tree[root].left=l;
    tree[root].right=r;
    tree[root].val=0;
    if(l==r) return ;
    int mid=l+r>>1;
    build(l,mid,root<<1);
    build(mid+1,r,root<<1|1);

}
//查询已经插入的数中有多少个数比val[i]大
//即查询区间[val[i],n-1]中数的个数
int query(int l,int r,int root)
{
    if(l==tree[root].left&&tree[root].right==r)
        return tree[root].val;
    int mid=tree[root].left+tree[root].right>>1;
    if(r<=mid)
        return query(l,r,root<<1);
    else if(l>mid)
        return query(l,r,root<<1|1);
    else
        return query(l,mid,root<<1)+query(mid+1,r,root<<1|1);

}
//更新所有包含id这个数的区间的val值,都+1
void update(int id,int root)
{
    tree[root].val++;
    if(tree[root].left==tree[root].right) return ;
    int mid=tree[root].left+tree[root].right>>1;
    if(id<=mid) update(id,root<<1);
    else  update(id,root<<1|1);
}
int main()
{
    int i,n;
    while(scanf("%d",&n)==1)
    {
        build(0,n-1,1);
        int sum=0;
        for(int i=0;i<n;i++)
        {
            scanf("%d",&val[i]);
            sum+=query(val[i],n-1,1);
            update(val[i],1);
        }
        int ret=sum;
        for(int i=0;i<n;i++)
        {
            sum=sum-val[i]+(n-val[i]-1);
            ret=min(ret,sum);
        }
        printf("%d\n",ret);
    }
    return 0;
}

 

https://wenku.baidu.com/view/6e02b7492e3f5727a5e9623f.html(这里面讲得相当详细)

猜你喜欢

转载自blog.csdn.net/weixin_42373330/article/details/81874885