Minimum Inversion Number
Time Limit : 2000/1000ms (Java/Other) Memory Limit : 65536/32768K (Java/Other)
Total Submission(s) : 26 Accepted Submission(s) : 20
Font: Times New Roman | Verdana | Georgia
Font Size: ← →
Problem Description
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
Output
Sample Input
10 1 3 6 9 0 8 5 7 4 2
Sample Output
16
該題目的意思 看了好久 ,最後參考博客才明白,就是把一個序列,
第一個 到n-1個 依次移到最後,全部移完之後,看有多少的最少的
逆序隊。 解題思路大致這樣, 累加該處到m的逆序對數目,然後,再
依次將1-n-1的數移到末尾。 根據減少的逆序對爲 a【0】,增加的爲m-a【0】+1;
最後 比較 求得最小的逆序對數目;
#include<stdio.h>
#include<algorithm>
#include<iostream>
#include<string.h>
#define maxn 5050
using namespace std;
int ss[maxn]; //定義 保存書
int a[maxn];
int lowbit(int x)
{
return x&(-x);
}
int sum(int x) //求和
{
int suM=0;
while(x>0)
{
suM+=ss[x];
x-=lowbit(x);
}
return suM;
}
void updata(int x,int y) //加減
{
while(x<maxn)
{
ss[x]+=y;
x+=lowbit(x);
}
}
int main()
{
int m,b,c;
while(~scanf("%d",&m))
{
memset(ss,0,sizeof(ss));
int ans=0;
for(int i=1;i<=m;i++)
{
scanf("%d",&a[i]);
a[i]++;
ans+=sum(m)-sum(a[i]);
updata(a[i],1);
}
int min=ans;
for(int i=1;i<=m;i++)
{
ans+=m-a[i]-(a[i]-1);
if(ans<min) min=ans;
}
printf("%d\n",min);
}
return 0;
}
而本題也是好用 線段樹來寫的,先建立一棵線段樹,然後輸入a數組的值,
#include<stdio.h>
#include<math.h>
struct shu
{
int left;
int right;
int num;
}s[15010];
int n;
int a[5010];
int min(int a,int b)
{
return a>b?b:a;
}
void build(int ll,int rr,int i) //建立一棵線段樹
{
s[i].right =rr;
s[i].left =ll;
s[i].num =0;
if(s[i].left==s[i].right)
{
return;
}
int mm;
mm=(s[i].left +s[i].right )/2;
build(ll,mm,2*i);
build(mm+1,rr,2*i+1);
}
int sum(int ll,int rr,int i) //求線段域的值
{
int ans=0;
if(ll==s[i].left &&rr==s[i].right )
return s[i].num;
int mm;
mm=(s[i].left+s[i].right)/2;
if(rr<=mm)
ans+=sum(ll,rr,2*i);
else if(ll>mm)
ans+=sum(ll,rr,2*i+1);
else
{
ans+=sum(ll,mm,2*i);
ans+=sum(mm+1,rr,2*i+1);
}
return ans;
}
void updata(int xx,int i) //
{
if(s[i].left ==s[i].right)
{
s[i].num ++;
return;
}
int mm=(s[i].left +s[i].right)/2;
if(xx<=mm) updata(xx,2*i);
else if(xx>=mm+1) updata(xx,2*i+1);
s[i].num=s[2*i].num +s[2*i+1].num; //父節點爲兩個節點的和
}
int main()
{
int i;
while(~scanf("%d",&n))
{
build(0,n-1,1);
int ans=0;
for(i=0;i<n;i++)
{
scanf("%d",&a[i]);
ans+=sum(a[i],n-1,1); //初次求得逆序對
updata(a[i],1);
}
int tt=ans;
for(i=0;i<n;i++)
{
tt=tt+n-1-a[i]-a[i]; //移動引起的節點的增加減少
ans=min(ans,tt); //比較,求小的值
}
printf("%d\n",ans);
}
return 0;
}