周练回顾(5)

这个周首先复习了暑假看的树状数组

#define lowbit(x) ((x)&-(x))
void add(int x,int d)   // 初始化(或更改树状数组),如果想初始化那就是多次更改
{
    while(x<=n)    
    {
        tree[x]+=d;
        x+=lowbit(x);
    }
}
int sum(int x)             //求数组和的优化
{
    int sum=0;
    while(x>0)
    {
        sum+=tree[x];
        x-=lowbit(x);
    }
    return sum;
}

P1168 中位数
给出一段序列,要求对于每奇数个数就要求一次中位数,n最大有10的5次方。
这个题首先想到的就是sort排序,但是每次要求中位数的时候就sort一次肯定就超时了,自己写了个二分答案也超时了…看到题解有大佬用动态数组+二分水过去了,对于每个数字,用二分找到它应该在的位置,插入到前面的数组里,每次直接输出数组中间的数就可以了(stl的二分,水题十分好用)。

#include 
using namespace std;
vector<int>a;
int main()
{
   int n,i,x;
   cin>>n;
   for(i=1;i<=n;i++)
   {
       cin>>x;
       a.insert(upper_bound(a.begin(),a.end(),x),x);
       if(i%2!=0)
       {
           cout<<a[(i-1)/2]<<endl;
       }
   }
}

B. Hemose Shopping
给出几组数据,对于每组数据有数组大小n,整数x,数组的数字。每个数组,如果|ai-aj|>=x,那么可以交换数组中的这两个数字,能不能将它们交换成升序?用yes和no代表输出。
纯思维题,但是想了很久。如果数组的大小除2要大于等于x,就肯定可以交换,如果小于x,我们还要考虑这个数组原本是否有序。如果这个数组在n-x到x的元素原来就有序,那么它仍可以交换;如果无序,那么就没办法了。

#include 

using namespace std;
typedef long long ll;
int a[100001];
int b[100001];
int main()
{
    int i,n,x,t;
    cin>>t;
    while(t--)
    {
        cin>>n>>x;
        for(i=0;i<n;i++)
        {cin>>a[i];
         b[i]=a[i];
        }
        if(n/2>=x) {cout<<"YES"<<endl;}
        else {
            sort(a,a+n);
            int k=1;
            for(i=n-x;i<x;i++)
            {
                if(b[i]==a[i]) continue;
                else {k=0;break;}
            }
        if(k==0) cout<<"NO"<<endl;
        else cout<<"YES"<<endl;
        }
    }
}

P1020 导弹拦截
通俗点说就是给出一段导弹的高度,第一个输出求出最长不上升子序列的长度,第二个输出最长上升子序列的长度。
想要得到满分的话要用复杂度为n*log2 n的方法,我用了二分查找和树状数组两种方法做出来了。
1.二分查找
网上有stl的二分和手写二分两种方法,我这里用的是手写的二分
建议不太懂的直接记模板,通过这种写法最长上升子序列、不上升子序列、下降子序列、不下降子序列只要改两个符号就可以实现转换。

#include
using namespace std;
int a[100001],f[100001];
int ans=0;
int main()
{
    int n=0,x;
    while(cin>>x)
    {
        n++;
        a[n]=x;
    }
    f[0]=999999;
    for(int i=1; i<=n; ++i)
    {
        if(a[i]==0)
            continue;
        if(f[ans]>=a[i])  //想要改成最长下降子序列把等号去掉就行
            f[++ans]=a[i];
        else
        {
            int l=0,r=ans,mid;
            while(l<=r)
            {
                mid=(l+r)/2;
                if(f[mid]<a[i])
                    r=mid-1;
                else
                    l=mid+1;
            }
            if(l!=0)
                f[l]=a[i];
        }
    }
    cout<<ans<<endl;  //最长不上升子序列
    memset(f,0,sizeof(f));
    f[0]=0;
    ans=0;
    for(int i=1; i<=n; ++i)
    {
        if(a[i]==0)
            continue;
        if(f[ans]<a[i])    //想要改成最长不下降子序列加个等号就可以了
            f[++ans]=a[i];
        else
        {
            int l=0,r=ans,mid;
            while(l<=r)
            {
                mid=(l+r)/2;
                if(f[mid]>=a[i]) 
                    r=mid-1;
                else
                    l=mid+1;
            }
            if(l!=0)
                f[l]=a[i];
        }
    }
    cout<<ans<<endl; //最长上升子序列
    return 0;
}

2.树状数组
虽然很久以前就学了树状数组,但是现在依然还是不怎么会用,看完大佬的代码我只能说是:妙啊,真是妙啊!
这里就解释一下最长上升子序列吧,另一个直接倒转其实就可以了。树状数组有两个函数,一个是用来初始化(或更改)树状数组的函数,另一个是通过树状数组来优化答案的函数。我们运用dp的思想,把导弹高度h作为树状数组的下标,用树状数组的值来记录最长的长度。对一个高度进行操作,我们要求得这个高度之前的最长上升长度,遍历树状数组记录下最大值就行。然后将这个高度插入树状数组,因为是最长上升子序列,所以树状数组下标比这个高度高的长度都要+1,然后求这个高度的最长子序列长度是多少,更改树状数组。控制范围,我们提前记录下导弹高度的最小值和最大值。

#include
#define lowbit(x) ((x)&-(x))
using namespace std;
int h[100001],shangsheng[100001],xiajiang[100001];
int maxx=0,n=0,m,minn=999999;
int zuichang(int x)  //最长上升子序列的计算函数
{
    int res=0;
    while(x>=minn)  //求这个高度之前的最长上升长度是多少
    {
        res=max(shangsheng[x],res);
        x=x-lowbit(x);
    }
    return res;
}
void addzuichang(int x,int y)  //最长上升子序列树状数组的插入函数
{
    while(x<=maxx)  //比这个高度高的都要重新更改一下树状数组的值
    {
        shangsheng[x]=max(shangsheng[x],y);
        x=x+lowbit(x);
    }
}
int buxiajiang(int x)
{
    int res=0;
    while(x<=maxx)
    {
        res=max(res,xiajiang[x]);
        x=x+lowbit(x);
    }
    return res;
}
void addbuxiajiang(int x,int y)
{
    while(x>=minn)
    {
        xiajiang[x]=max(xiajiang[x],y);
        x=x-lowbit(x);
    }
}
int main()
{
    int i;
    int ans1=0,ans2=0;
	while(cin>>m)
    {
        n++;
        h[n]=m;
        maxx=max(maxx,m);  //提前求出范围
        minn=min(minn,m);
    }
    for(i=1;i<=n;i++)
    {
        int x=zuichang(h[i])+1; //用函数求出的是它之前的最长长度,算上它自己还要+1
        int y=buxiajiang(h[i])+1;
        ans1=max(ans1,y);
        ans2=max(ans2,x);  //求出求过的最大值
        addzuichang(h[i]+1,x);//因为是严格上升,高度还要+1
        addbuxiajiang(h[i],y);//不上升就行了,比这个高度低的都可以更改
    }
    cout<<ans1<<endl;
    cout<<ans2<<endl;
}

你可能感兴趣的:(算法,数据结构,c++)