10 1 3 6 9 0 8 5 7 4 2
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> #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 */其实下面那部分的还是和第一种方法是一样的,所以线段树维护的还是求出sum的过程(即为原先数列的逆序数的个数)。
多积累,多AC,加油!!
hdu(1394)——Minimum Inversion Number
原文:http://blog.csdn.net/acmer_hades/article/details/46365603