hdu(1394)——Minimum Inversion Number
2015-06-04 18:41
387 查看
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
Sample Output
我后来想了下,为什么这道题要用线段树呢?原来线段树就是一个辅助作用,他能帮助我们更快地找出区间和,而不是说它能够帮我们完全解决问题。
这道题的大致意思是:给你一个逆序数,然后每次都可以把第一个数可以移到最后一个位置去,然后问你这n-1种序列每一种的逆序数是多少,然后问你所有这几种序列中逆序数的最小值是多少。
这道题好像有四种做法,然后现在我只了解了2种,其余的以后再更新。
1:首先第一种想法是找规律,我们先求出最初的那个序列的逆序数的个数,记为sum,然后每次把第一个移动到最后一个就会使sum减少a[i],但是又会增加n-(a[i]+1)个。这个规律如果不理解的话可以举几组例子来想想看。然后for个n遍,就可以找到最小的逆序数了。
第二种想法是线段树,
这个说实话一些题解想法写的不够详细,然后我看了几天才是真正的理解。
这里给大家推荐一下这个人写的博客:http://blog.sina.com.cn/s/blog_691ce2b70101ldmm.html
可以去看一下他的思路,但是不一定要学习他的线段树的写法。
思路就是:我们对按照顺序读入的n个数然后每次读入一个数的时候就看一下大于它的且已经出现过的数的个数。
比如说是:有10个数,然后我们之前已经读入了3,6,7,然后输入0的时候就去1~10的范围去询问那些数已经出现过了,那么那些数就是它的逆序数。那么在这个例子中是3,6,7,所以它的逆序数是3个。
多积累,多AC,加油!!
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
我后来想了下,为什么这道题要用线段树呢?原来线段树就是一个辅助作用,他能帮助我们更快地找出区间和,而不是说它能够帮我们完全解决问题。
这道题的大致意思是:给你一个逆序数,然后每次都可以把第一个数可以移到最后一个位置去,然后问你这n-1种序列每一种的逆序数是多少,然后问你所有这几种序列中逆序数的最小值是多少。
这道题好像有四种做法,然后现在我只了解了2种,其余的以后再更新。
1:首先第一种想法是找规律,我们先求出最初的那个序列的逆序数的个数,记为sum,然后每次把第一个移动到最后一个就会使sum减少a[i],但是又会增加n-(a[i]+1)个。这个规律如果不理解的话可以举几组例子来想想看。然后for个n遍,就可以找到最小的逆序数了。
#include<stdio.h> #include<string.h> #include<algorithm> using namespace std; #define maxn 5555 #define inf 99999999 int a[maxn]; int main(){ int n; while(~scanf("%d",&n)){ int min1=inf,num=0; for(int i=0;i<n;i++) scanf("%d",&a[i]); for(int i=0;i<n;i++){ for(int j=i+1;j<n;j++){ if(a[i]>a[j]&&i<j) num++; } } if(min1>num) min1=num; //下面是找规律得出的; //就是每一次把最前面的移到最后,逆序数对数会减少a[i]个,但是会增加n-(a[i]+1)个,举几个例子就知道了 for(int i=0;i<n;i++){ num=num-a[i]+n-(a[i]+1); if(min1>num) min1=num; } printf("%d\n",min1); } } /* 4 1 3 2 0 */
第二种想法是线段树,
这个说实话一些题解想法写的不够详细,然后我看了几天才是真正的理解。
这里给大家推荐一下这个人写的博客:http://blog.sina.com.cn/s/blog_691ce2b70101ldmm.html
可以去看一下他的思路,但是不一定要学习他的线段树的写法。
思路就是:我们对按照顺序读入的n个数然后每次读入一个数的时候就看一下大于它的且已经出现过的数的个数。
比如说是:有10个数,然后我们之前已经读入了3,6,7,然后输入0的时候就去1~10的范围去询问那些数已经出现过了,那么那些数就是它的逆序数。那么在这个例子中是3,6,7,所以它的逆序数是3个。
#include<stdio.h>其实下面那部分的还是和第一种方法是一样的,所以线段树维护的还是求出sum的过程(即为原先数列的逆序数的个数)。
#include<string.h>
#include<iostream>
#include<algorithm>
using namespace std;
#define maxn 5555
#define inf 99999999
int a[maxn];
struct node{
int l,r,sum;
}tree[maxn*4];
int ans=0;
void pushup(int v){
int temp=v*2;
tree[v].sum=tree[temp].sum+tree[temp+1].sum;
}
void build(int l,int r,int v){
tree[v].l=l;
tree[v].r=r;
tree[v].sum=0;
if(l==r) return;
int temp=v*2;
int mid=(l+r)/2;
build(l,mid,temp);
build(mid+1,r,temp+1);
}
int query(int l,int r,int v){
if(l==tree[v].l&&r==tree[v].r){
return tree[v].sum;
}
int temp=v*2;
int mid=(tree[v].l+tree[v].r)/2;
if(r<=mid) return query(l,r,temp);
else if(l>mid) return query(l,r,temp+1);
else{
return query(l,mid,temp)+query(mid+1,r,temp+1);
}
}
void update(int pos,int v){
if(tree[v].l==tree[v].r){
tree[v].sum++;
return ;
}
int mid=(tree[v].l+tree[v].r)/2;
int temp=v*2;
if(pos<=mid) update(pos,temp);
else update(pos,temp+1);
pushup(v); //记得这里要pushup!!!
}
int main(){
int n;
while(~scanf("%d",&n)){
memset(a,0,sizeof(a));
for(int i=0;i<n;i++) scanf("%d",&a[i]);
build(0,n-1,1);
int sum=0,min1=inf;
for(int i=0;i<n;i++){
ans=query(a[i],n-1,1);
sum+=ans;
update(a[i],1);
}
for(int i=0;i<n;i++){
sum+=n-(a[i]+1)-a[i];
if(sum<min1) min1=sum;
}
printf("%d\n",min1);
}
}
/*
10 1 3 6 9 0 8 5 7 4 2
4
1 3 2 0
*/
多积累,多AC,加油!!
相关文章推荐
- Database mail 配置
- json
- 百度搜索引擎——评价
- 基于局部感受野的极速学习机
- 开发中使用到UITableView的各个技术点
- [转载]苹果上线HomeKit支持文档,Apple TV将成为HomeKit中枢设备
- <转> 解决异常:IllegalStateException: Fragment <ThisFragment> is not currently in the FragmentManager
- CentOS6.6安装heartbeat实现高可用
- linux默认mysql迁移目录
- 使用lldb来处理以下三种场景: 死循环、异常断点、多线程
- 使用maven将代码到私服
- [D3] 10. Creating Axes with D3
- 理解错误的一些概念总结。
- mybatis问题org.apache.ibatis.binding.BindingException: Parameter 'title' not found.
- context:component-scan扫描使用上的容易忽略的use-default-filters
- Android 异步处理-有点意思
- 循环语句提纲
- 图书助手第二次冲刺第五天
- DOM知识点
- 判断一个数是不是素数 埃拉托斯特尼筛法 时间复杂度 O(n*lglgn)