题目描述:
有一个未知排列,已知排列中元素个数以及每个元素前面有多少个元素比它本身小。
求此排列。
/* 分析:
* 解法一:全排列 然后挑选符合题意的排列
* 解法二:倒过来看
* 最后一个元素n[i] 代表这个元素在整个排列的位置排在s[i]+1个
* 倒数第二个元素m[i-1] 代表这个元素在剩下的排列中排在s[i-1]+1个
* 依次类推
* 有:看代码..
*/
* 解法一:全排列 然后挑选符合题意的排列
* 解法二:倒过来看
* 最后一个元素n[i] 代表这个元素在整个排列的位置排在s[i]+1个
* 倒数第二个元素m[i-1] 代表这个元素在剩下的排列中排在s[i-1]+1个
* 依次类推
* 有:看代码..
*/
全排列就不写了,主要放解法二代码:
/****** version 1************/
#include <cstdio>
#include <cstdlib>
#include <iostream>
#define maxn 10010
#define finput(file) freopen(file,"r",stdin)
#define foutput(file) freopen(file,"w",stdout)
using namespace std;
int main(int argc, char *argv[])
{
int num,i,j,k;
int ans[maxn],s[maxn],n[maxn];
// finput("in.txt");
// foutput("out.txt");
cin>>num;
i=0;
while(i++<num)
{
cin>>s[i];
ans[i]=0;
n[i]=0;
}
for(i=num;i>0;i--)//处理剩下排列的最后一个
{
for(j=1,k=0;k<s[i]+1;j++)
{
if(n[j]==0)
k++;
}
ans[i]=j-1;
n[j-1]=1;
}
for(i=num;i>=0;i--)
cout<<ans[i]<<" ";
return 0;
}
这是常规解法。下面是线段树解。
/***** version 2 ************/
/****线段树解*****/
/*实现在一个集合中查找第K大数而且还要支持动态删除点*/
/*动态删除的时候。。设置一个跳转的值。。*/
#include <cstdio>
#include <cstdlib>
#include <iostream>
#define maxn 10010
#define finput(file) freopen(file,"r",stdin)
#define foutput(file) freopen(file,"w",stdout)
using namespace std;
typedef struct node
{
int sta,end;
int leftnum;
struct node *leftChild, *rightChild;
int number;
}node;
node* buildTree(int a,int b)
{
node* p ;
p = (struct node*)malloc(sizeof(node));
if(a == b)
{
p->leftChild = NULL;
p->rightChild = NULL;
return p;
}
p->sta = a;
p->end = b;
p->leftChild = buildTree(a,(a+b)/2);
p->rightChild = buildTree((a+b)/2+1,b);
p->leftnum = (b-a)/2+1;//左孩子个数
//左孩子个数是(b-a+1)/2会错
return p;
};
//第a大的值赋为num
void addNumber (node* p,int a,int num)
{
// if(a > (p->leftnum + p->rightnum))
// return;
if(p->leftChild == NULL)
{
// cout<<"t1:-"<<a<<endl; //a必定为1
p->number = num;
return;
}
if(a > p->leftnum)
addNumber(p->rightChild, a - p->leftnum,num);
if(a <= p->leftnum)
addNumber(p->leftChild, a, num);
};
//查找第N大的数,并删除
int search(node *p, int a)
{
int res;
if(p->leftChild == NULL)
{
return p->number;
}
if(a > p->leftnum)
res = search(p->rightChild, a-p->leftnum);
if(a <= p->leftnum)
{
p->leftnum--;
res = search(p->leftChild, a);
}
return res;
}
int main(int argc, char* argv[])
{
node *tree;
int casenum;
int num,a[maxn],ans[maxn];
// finput("in.txt");
cin>>casenum;
while(casenum--)
{
cin>>num;
tree = buildTree(1, num);
int i,j=0;
for(i=1;i<=num;i++)
{
cin>>a[i];
addNumber(tree,i,i);
}
for(i=num;i>0;i--)
ans[i]=search(tree, a[i]+1);
for(i=1;i<=num;i++)
cout<<ans[i]<<" ";
cout<<endl;
}
return 0;
}
线段树一般都是操作静态数据,针对这个题目,每查询一次就要将所进行的查询结果从原集合中删除
于是,设置一下左子树所拥有的数目,就可以进行选择了。因为区间[a,b]是我们操作的,所以能保证不超,所以非左即右。
针对每一个节点,都有一个左孩子数目。所以我们能很方便找到区间内第k大。
描述不是很清楚》。《 主要看代码吧..