题意:给你一个n个数的序列,其中组成的数只有0-n,我们可以进行这么一种操作:把第一个数移到最后一个,次数不限。问,在原始数列和最新生成的数列中逆序数最小可以是多少?
暴力是可以过的。当然是,只暴力求原序列的逆序对,通过递推求新序列的逆序对数。
递推关系是这样子的:首先要明白将第一个数移到最后一个,其逆序数的变化是在前一个逆序数n的基础上这样
变成。因为这是一个0 ~ n-1地全排列,所以low(a[1])为a[1]个,up(a[1])为n-a[1]个。
指路:递推的解释
或者这样说,我把第一个数删掉,逆序数减少low(a[1])个;再把第一个数放到最后,逆序数增加up(a[1])个。
法1:暴力
//暴力也可过
//HDU 1394
#include<iostream>
#include<algorithm>
#include<set>
#include<cstring>
#include<queue>
#include<stack>
#include<vector>
#include<stdio.h>
using namespace std;
#define ll long long
const int maxn=5005;
int num[maxn];
int sum[maxn];
int n;
int main()
{
while(scanf("%d",&n)!=EOF)
{
memset(num,0,sizeof(num));
memset(sum,0,sizeof(sum));
int sum=0;
for(int i=1;i<=n;i++)
{
scanf("%d",&num[i]);
for(int j=1;j<i;j++)
{
if(num[j]>num[i])
{
sum++;
}
}
}
int ans=sum;
for(int i=1;i<=n-1;i++)
{
sum=sum-num[i]+(n-num[i]-1);
ans=min(ans,sum);
}
printf("%d\n",ans);
}
}
法2: 线段树
开始,并不能理解,这个题目也和线段树有关?
然后,翻了题解。
建立线段树的过程,跟逆序对完全没有关系!
只不过,每插入一个数据,通过线段树进行查询比这个数据大的有多少,也就是能 增加多少逆序对。
所以,线段树,初始化所有结点为0。标号范围在1-n的结点 i 的tree[i],就代表数据i出现了几次。(当然,叶子节点肯定最多只有一次)。
每次更新,也就是对应的tree[]++;
父节点结点代表的区间[a,b]代表数据a-b,一共出现了几次。
//AC
//HDU 1394 线段树
#include<iostream>
#include<algorithm>
#include<set>
#include<cstring>
#include<queue>
#include<stack>
#include<vector>
#include<stdio.h>
using namespace std;
#define ll long long
const int maxn=5005;
int num[maxn];
int tree[maxn<<2];
int n;
//每个结点代表逆序对数
void pushup(int root)
{
tree[root]=tree[2*root]+tree[2*root+1];
return;
}
//开始全部初始化为0
void build(int root,int left,int right)
{
if(left==right)
{
tree[root]=0;
return;
}
int mid=(left+right)/2;
build(2*root,left,mid);
build(2*root+1,mid+1,right);
pushup(root);
return;
}
//单点更新
void update(int root,int index,int left,int right)
{
// cout<<'u'<<left<<" "<<right<<" "<<tree[root]<<endl;
if(left==right)
{
//if(index==left)这句话一定不能加,我这是从哪想的啊??
tree[root]++;
return;
}
int mid=(left+right)/2;
if(index<=mid)//这个index总是忘掉,所以每次测试总会死循环!
update(2*root,index,left,mid);
else if(index>mid)
update(2*root+1,index,mid+1,right);
pushup(root);
}
//查询
int query(int root,int left,int right,int qleft,int qright)
{
// cout<<'q';
if(right<qleft || left>qright)
return 0;
if(left>=qleft && right<=qright)
return tree[root];
int mid=(left+right)/2;
int ans=0;
if(mid>=qleft)
ans+=query(2*root,left,mid,qleft,qright);
if(mid<qright)
ans+=query(2*root+1,mid+1,right,qleft,qright);
return ans;
}
int main()
{
while(scanf("%d",&n)!=EOF)
{
build(1,1,n);
int sum=0;
for(int i=0;i<n;i++)
{
scanf("%d",&num[i]);
//以1-n进行存储
sum+=query(1,1,n,num[i]+1,n);//查询已经存储过的,比它大的有多少
update(1,num[i]+1,1,n);
}
int ans=sum;
// cout<<ans<<" ";
for(int i=0;i<n-1;i++)
{
sum=sum-num[i]+(n-num[i]-1);
if(sum<0) continue;//不过这句话,好像存在什么WA点。
ans=min(ans,sum);
}
cout<<ans<<endl;
}
}
最后,自己在update()函数,总是会出现错误。
if(index==left) ,真是不知道自己从哪里想到的?突然有点不明白,为什么不能加?是index是num[ ]的下标,根本不是tree[ ]的下标吗?