离散化&线段树----队列游戏

题目如下所示:

 当然,第一个想法是两个for,课后作业给的结果是有一个样例没通过(超时)。

 
            #include<iostream>
#include<cstring>
#include<cstdio>
#include<stack>
#include<queue>
using namespace std;


int main()
{
    int count[100100];
    memset(count,0,sizeof(count));
    int n,j;
    int nums[100100];
    scanf("%d",&n);
    for(int t=0;t<n;t++)
    {
        scanf("%d",&nums[t]);
    }
    for(int i=0;i<n;i++)
    {
        int temp=nums[i];
        for(j=i;j<n;j++)
        {//cout<<temp<<"  "<<nums[j]<<endl;
            if(nums[j]<temp)
            count[i]++;
        }
    }

    for(int p=0;p<n;p++)
    {
        cout<<count[p]<<" ";
    }
    return 0;

}




        

之后同学建议了使用线段树。

先介绍两个基本的知识点,是为了离散化。

lower_bound用法

unique用法

关于lower_bound和upper_bound  

共同点

函数组成:(https://blog.csdn.net/Baiyi_destroyer/article/details/81174695)

一个数组元素的地址(或者数组名来表示这个数组的首地址,用来表示这个数组的开头比较的元素的地址,不一定要是首地址,只是用于比较的“首”地址)+ 一个数组元素的地址(对应的这个数组里边任意一个元素的地址,表示这个二分里边的比较的”结尾’地址)+ 你要二分查找的那个数。

例如:

lower_bound(r[x].begin(),r[x].end(),l)

upper_bound(r[x].begin(),r[x].end(),R)

区别

lower_bound与upper_bound的返回值是不同的

lower_bound

返回第一个大于等于x的数的地址
例如数组 1 1 1 3 5

而需要找的那个数是2,怎么返回呢,

就是返回那个第一个大于 2 的数的地址,就是返回3的位置,那么再有一组数据就是5个数1 1 1 3 5,还是需要找寻2,那么该返回什么呢?就是第一个3的地址.

upper_bound

返回第一个大于x的数的地址
也就是说如果在5个数 1 , 1, 2 , 2 , 4 ,里边寻找3,那么就会返回4的地址

代码

lower_bound

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int k,n=10;
int a[10]={1,1,1,3,3,5,5,5,5,6};
int main()
{
    for(int i=0;i<n;i++)cout<<a[i]<<" ";
    cout<<endl;
   while(scanf("%d",&k))
   {
       cout<<k<<"的第一个大于等于它的位置在"<<((lower_bound(a,a+n,k))-a)+1<<endl;
   }
}

C++的unique函数

unique是STL中很实用的函数之一,需要#include<iostream>,下面来简单介绍一下它的作用。

unique的作用是“去掉”容器中相邻元素的重复元素,这里去掉要加一个引号,为什么呢,是因为它实质上是一个伪去除,它会把重复的元素添加到容器末尾,而返回值是去重之后的尾地址(是地址!!),举个例子:

int num[10]={1,1,2,2,2,3,4,5,5,5};
int ans=unique(num,num+10)-num;

第一种离散化。(包含重复元素,推荐使用)

我们在处理数组问题时,如需要求逆序数,然而数据给的又特别大,此时我们就可以用数组的离散处理来解决了。

当数的范围比较大时需要进行离散化,即先排个序,再重新编号。如a[] = {10000000, 10, 2000, 20, 300},那么离散化后a[] = {5,

1, 4, 2, 3}。

const int maxn=1e5+10;
 
int a[maxn], t[maxn];
 
int n;
 
scanf("%d",&n);
 
for(int i=1; i<=n; i++)
 
    scanf("%d",a[i]),t[i]=a[i];
 
sort(t+1,t+n+1);
 
m=unique(t+1,t+1+n)-t-1;//m为不重复的元素的个数
 
for(int i=1; i<=n; i++)
 
    a[i]=lower_bound(t+1,t+1+m,a[i])-t;
 

第二种离散化,使用结构体实现

#include<cstdio>
#include <iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAXN=500000+1000;
int c[MAXN];
struct node
{
    int v;
    int index;
    bool operator <(const node& b)const
    {
        return v<b.v;
    }
}nodes[MAXN];
int b[MAXN];//将初始数组重新赋值后 相对大小不变的新数组
int main()
{
        int n;
        cin>>n;
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&nodes[i].v);
            nodes[i].index=i;
        }
        sort(nodes+1,nodes+n+1);
        memset(b,0,sizeof(b));
        b[nodes[1].index]=1;
        for(int i=2;i<=n;i++)
        {
            if(nodes[i].v==nodes[i-1].v)
                b[ nodes[i].index ]=b[ nodes[i-1].index ];
            else
                b[ nodes[i].index ]=i;
        }
       
      
        for(int i=1;i<=n;i++)
        cout<<b[i]<<endl;
}

然后是线段树的学习。。。。

学习地址

自己写了一个。。然后测试结果全部是超内存。

#include<cstdio>
#include <iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=1e5+10;
int a[maxn], t[maxn];
int n;
int llMax,llMin;
struct node
{
    int count;
    int l;//区间左端点
    int r;//区间右端点
    struct node* left;
    struct node* right;
    node(int start,int end)
    {
        count=0;
        l=start;
        r=end;
        left=NULL;
        right=NULL;
    }
};

void fun()
{
    scanf("%d",&n);
    for(int i=1; i<=n; i++)
    {
        scanf("%d",&a[i]),
        t[i]=a[i];
    }
    sort(t+1,t+n+1);
    llMin=1;
    int m=unique(t+1,t+1+n)-t-1;//m为不重复的元素的个数
    llMax=m;
    for(int p=1; p<=n; p++)
        a[p]=lower_bound(t+1,t+1+m,a[p])-t;
}

node* add(int start,int end)
{
    node* root = new node(start,end);
    if( start == end)
        return root;
    root->left = add(start , (start + end)/2);
    root->right = add((start + end)/2 + 1 , end);
    return root;
}
int query(node* p,int num)
{
    p->count++;
    if(p->left==NULL && p->right==NULL)
        return 0;
    if( num <= (p->l+p->r)/2 )
        return query(p->left,num);
    else
        return query(p->right,num)+p->left->count;
}
int main()
{
    struct node * p;
    int i;
    //数据先离散化
    fun();
    //构造线段树
    p=add(llMin,llMax);
    int k=0,temp[1010];
    for(i=n; i>0; i--)
    temp[k++]=query(p,a[i]);
    for(i=k-1;i>=0;i--)
    cout<<temp[i]<<" ";
    return 0;
}

随后的想法是,使用数组进行构建线段树。。使用结构体都很容易超内存。

结果老是超内存,,只好先试试1010大小的数组了。结果是10个样例过了3。。。各种错,等待解决。。。

#include<cstdio>
#include <iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=1010;
int a[maxn], t[maxn];
int n;
int llMax,llMin;


void fun()
{
    scanf("%d",&n);
    for(int i=1; i<=n; i++)
    {
        scanf("%d",&a[i]),
        t[i]=a[i];
    }
    sort(t+1,t+n+1);
    llMin=1;
    int m=unique(t+1,t+1+n)-t-1;//m为不重复的元素的个数
    llMax=m;
    for(int p=1; p<=n; p++)
        a[p]=lower_bound(t+1,t+1+m,a[p])-t;
}

int add(int root,int start,int end,int num)
{
   // cout<<start<<"  "<<end<<endl;
    t[root]++;
    int mid=(end+start)/2;
    if( start == end)
        return 0;
    else
    {
    if(num<=mid)
    return add(root*2,start,(start + end)/2,num);
    else
    return t[root*2]+add(root*2+1,(start + end)/2 + 1 , end,num);
    }
}
int main()
{
    int i;
    //数据先离散化
    fun();memset(t,0,sizeof(t));
    //构造线段树

    int k=0,temp[1010];
    for(i=n; i>0; i--)
    {
    temp[k++]=add(1,llMin,llMax,a[i]);
    }

    for(i=k-1;i>=0;i--)
    cout<<temp[i]<<" ";
    return 0;
}


        

猜你喜欢

转载自blog.csdn.net/pan_xi_yi/article/details/83021542
今日推荐