C++题目,还原数列,知道每个数之前有多少个比它小的数,求原先的数列,求大神解答!

【问题描述】
给定1~N的某个排列,可以很容易求出每个数之前有多少个比它小的数。
但反过来,如果知道每个数之前有多少个比它小的数,能否求出原先的排列呢?

【输入数据】(sequence.in)

第一行,N(1<=N<=100)

第二行,N个数,分别表示每个数之前有多少个比它小的数。

【输出数据】(sequence.out)

一行,N个数为所求的原先排列

输入样例

4

0 1 2 1

【样例输出】

1 3 4 2

int num = 6;    // N
vector<int> vecNum(num);    // 1, 2, ..., N 
for (int i = 0; i < num; i++){
    vecNum[i] = i + 1;
}

vector<int> vecInput(num);  // input
vecInput[0] = 0;
vecInput[1] = 0;
vecInput[2] = 1;
vecInput[3] = 2;
vecInput[4] = 4;
vecInput[5] = 0;

vector<int> vecOutput(num); // output
vector<int>::reverse_iterator itOutput = vecOutput.rbegin();
for (vector<int>::reverse_iterator itInput = vecInput.rbegin(); itInput != vecInput.rend(); itInput++) {
    *itOutput++ = vecNum[*itInput];
    vecNum.erase(vecNum.begin() + *itInput);
}

// print
for (vector<int>::iterator it = vecOutput.begin(); it != vecOutput.end(); it++) {
    cout << *it << endl;
}

假设题目输入的序列A(非原序列)为a_1,a_2......a_n,表示第i个数前有a_i个数(1<=i<=n),它们对应的原序列中的数的值为b_1,b_2......b_n

————————————————————————————————————————

首先考虑特殊值,对于每个序列,一定存在一个最大值k,现在在未知序列中的第i个位置,那么他一定可以满足性质:在它前面一定存在(i-1)个比它小的数,即a_i=i-1(1<=i<=n)

然后思路就清晰了

————————————————————————————————————————————————————

我们可以先找到序列A中满足a_i=i-1(1<=i<=n),这时可能会有多个满足条件的i,那么这时序列中的最大值应选择哪个呢?

先定义这样多个位置i组成的序列为I:i_1,i_2......i_m(1<=i<=m,m为序列长度)

并且该序列有序,即i_1<i<2<......<i_m(即保持原序列中i存在的相对位置不变)

它们对应的原序列的数分别为b_i_1,b_i_2......b_i_m

//Ps:定义只是为了方便而已

那么假定b_i_j为最大值(1<=j<m),那么因此b_i_m<b_i_j,又因为i_j在i_m前,所以b_i_m前不存在a_i_m个比b_i_m小的数,所以,可反证j=m

——————————————————————————————————————————————————————————————

选完最大值后,应该怎样选择其他值呢?

其实很简单,当选完最大值后,将最大值从A和I中删去,那么会得到新序列A和I,这时新的A序列中会有新的最大值,并且因为序列中的所有数都比最大值小(除最大值外),所以a_k(1<=k<=n)会保持不变,那么可以用一样的方法求出新序列中的最大值,以此类推,可以得到整个序列

注意删去最大值后,原来在i_m后的数可能会进入序列I,即满足最大值的性质

——————————————————————————————————

现在是实现

怎样实现,即求出序列I,其实很简单,可以参考一下平衡树的建立,具体来说,是笛卡尔树的建立(不懂可以自己去搜,很好学的)

在这样的树里(它是二叉树),在它的子树中数字比根节点小,并且左子树中的节点所对应的序列上的位置都在根前,其实我们只需要看左子树就行了,右子树只是帮助删除操作,每一次删除时就转化为删除根节点,删除后更新树,再重新删除根节点,那么就按照删除顺序就可以得到原序列。

——

删除操作只需要将根节点去掉,再将根节点的右子树插入到左子树中就行了。(学了就懂了)//Ps:这好像是废话

#include <stdio.h>

int main()
{
    int n = 0;
    int arr[101];
    int r[101];
    scanf("%d", &n);
    for (int i = 0; i < n; i++)
    {
        scanf("%d", &arr[i]);
    }
    int curr = 1;
    for (int i = 0; i < n; i++)
    {
        for (int j = n - 1; j >= 0; j--)
        {
            if (arr[j] == i)
                r[j] = curr++;
        }
    }
    for (int i = 0; i < n; i++)
        printf("%d ", r[i]);
    printf("\n");
    return 0;
}

问题解决的话,请点下采纳,谢谢合作