SPOJ 227 Ordering the Soldiers

简介: 点击打开SPOJ227 思路: 树状数组 分析: 1  给定一个n个数的序列假设为b数组,那么b[i]表示的是i之前比第i个数大的个数,比如样例的2 1 3对应的b数组是0 1 0,现在要求a数组,已知a数组的值是1~n 2  我们通...

点击打开SPOJ227

思路: 树状数组

分析:

1  给定一个n个数的序列假设为b数组,那么b[i]表示的是i之前比第i个数大的个数,比如样例的2 1 3对应的b数组是0 1 0,现在要求a数组,已知a数组的值是1~n


2  我们通过b数组可以知道a数组的情况,因为前面的数会影响后面的数,那么我们从后面枚举b数组,这样我们可以知道对于第i个数i-b[i]就是剩下的i个数中的第几大的数,比如第二个样例

    0 1 2 0 1 , 那么i为5的时候i-b[i] = 5-1 = 4,说明第5的数是1~n中的第四大的数也就是4,接下来我们删除掉4,i为4的时候i-b[i] = 4-0 = 4也就是第四个数是剩下的第4大的数也就是5,以此类推.....


3  通过2的思路,我们发现很简单,但是我利用vector的时候TLE了,说明我们需要一个更高效率的算法


4  由于n个数是1~n,那么我们初始化一个树状数组treeNum[i] = i;

    表示的是前面有i个数比它小,那么我们可以知道树状数组是一个单调递增,为什么呢?因为我们初始化每个点的值都还没被取过,那么区间[1,i]的和为i,所以i越大和越大。

    通过第2点的分析我们可以知道,我们能够求出每一个数的排名,也就是前面比它小的个数,那么这个就是树状数组保存的值,所以我们可以通过二分答案来求,我们取了某个数之后就标为true,然后树状数组进行单点更新。

    但是我们会遇到一个问题就是比如第二个样例中我们把4和5取完之后我们会发现[1,3]和[1,4]和[1,5]这三个区间的和为3,但是只有3是符合的,因为4和5被取了,所以我们在二分的时候应该注意判断值是否被取过


5  那么我们来分析一下时间复杂度,我们需要枚举b数组为O(n),每次二分的时间为O(logn),每次更新树状数组的时间为O(logn),总的为O(n*logn*logn)


代码:

/***********************************************
* By: chenguolin                               * 
* Date: 2013-08-19                             *
* Address: http://blog.csdn.net/chenguolinblog *
***********************************************/
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;

const int MAXN = 200010;

int n;
int num[MAXN];
int treeNum[MAXN];
bool isSelect[MAXN];

int lowbit(int x){
    return x&(-x);
}

int getSum(int x){
    int sum = 0;
    while(x){
         sum += treeNum[x];
         x -= lowbit(x);
    }
    return sum;
}

void add(int x , int val){
    while(x < MAXN){
         treeNum[x] += val;
         x += lowbit(x);
    }
}

void init(){
    memset(isSelect , false , sizeof(isSelect));
    memset(treeNum , 0 , sizeof(treeNum));
    for(int i = 1 ; i <= n ; i++)
        add(i , 1);
}

int search(int x){
    int left = 1;
    int right = n;
    while(left <= right){
         int mid = (left+right)>>1;
         int sum = getSum(mid);
         if(sum == x){
             if(!isSelect[mid]) 
                 return mid;
             right = mid-1; 
         }
         else if(sum < x)
             left = mid+1;
         else
             right = mid-1;
    }
}

void solve(){
    init();
    int output[MAXN];
    for(int i = n ; i >= 1 ; i--){
        int x = i-num[i];
        int ans = search(x);
        output[i] = ans;
        isSelect[ans] = true;
        add(ans , -1);
    }
    printf("%d" , output[1]);
    for(int i = 2 ; i <= n ; i++)
        printf(" %d" , output[i]);
    puts("");
}

int main(){
    int cas;
    scanf("%d" , &cas);
    while(cas--){
         scanf("%d" , &n);
         for(int i = 1 ; i <= n ; i++) 
             scanf("%d" , &num[i]);
         solve();
    }
    return 0;
}




目录
相关文章
UVa872 - Ordering(拓扑排序)
UVa872 - Ordering(拓扑排序)
61 0
|
开发框架 .NET
poj 3468 A Simple Problem with Integers线段树区间修改
题目意思很简单,有N个数,Q个操作, Q l r 表示查询从l到r 的和,C l r v 表示将从l到r 的值加上v,明显的线段树,不知道线段树的人肯定暴力,肯定超时,哈哈!!
34 0
UVa10776 - Determine The Combination(有重复元素的组合问题)
UVa10776 - Determine The Combination(有重复元素的组合问题)
46 0
UVa11714 - Blind Sorting
UVa11714 - Blind Sorting
55 0
|
算法 搜索推荐 数据库
一个有点咬文嚼字的 sorting 和 ordering
为什么排序算法的英文是 sorting 而不是 ordering。
144 0
|
索引 Python
LeetCode 44. Wildcard Matching
给定输入字符串s和模式串(p),实现通配符模式匹配支持'?' 和'*'.
85 0
|
C++
Early Orders单调栈
大体思路: 用pos数组来记录每个数出现的最后的位置 然后遍历每一个数组元素,如果当前元素已经被标记过了(已经被使用),那么就跳过这次循环 设遍历数组元素的时候,将这个数组的下标记为 i ,值的大小为 x 当栈里面有元素,并且栈顶的元素大于x,并且栈顶这个数最后出现的位置大于当前这个数最后出现的位置 i ,那么说明栈里面这一数列就不是最优的,就可以将当前的元素x替换掉栈顶元素,先pop(),然后将这个数标记为 0 ,–>未使用 如果当上述条件一直满足的时候,可以一直进行上面的操作 知道不满足题意,然后讲当前这个数放到栈的顶端
91 0
Early Orders单调栈
HDOJ 1016 Prime Ring Problem素数环【深搜】
HDOJ 1016 Prime Ring Problem素数环【深搜】
120 0
HDOJ 1016 Prime Ring Problem素数环【深搜】
HDU-1048,The Hardest Problem Ever(字符串处理)
HDU-1048,The Hardest Problem Ever(字符串处理)
下一篇
DataWorks