• 无题


    在杭州集训五天了,现在才想起来写博客.....

    主要还是因为今天的题目我写出来了,想发篇博客记录下。

    第一题其实还挺简单的,只不过当时我认为我的算法天衣无缝,结果只拿到了中间的30分;

    下面来说下正解:

    O(nlogn)算法:一开始的朴素算法就是开一个二维数组,暴力枚举每一个的情况,但这种方法只限于数组范围小的情况,后面几组测试点的数据都很大,显然这么做是不完美的,仔细思考一番后我们发现:对于行和列,对于一个点,我们只要判断其是否存在在同一行同一列的前驱和后继就好了,我们可以通过双关键字排序做到。

    对于行:以x为第一关键字,y为第二关键字排序,判断它前一个点x值是否相同,后一个点x值是否相同。列也一样。

    对于斜着的情况,我们也可以用双关键字排序做到,如何修改排序操作,我们要把每一斜线上的点放在一起。

    对于左上向右下斜:以x-y为第一关键字,x+y为第二关键字排序.

    对于右上向左下斜:以x+y为第一关键字,x-y为第二关键字排序。

    做4遍排序,再统计答案。

    代码:

    #include<bits/stdc++.h>
    using namespace std;
    struct knight{
        int x,y,num;
    }k[100005];
    int n,m;
    int wudi[100005];
    int num2[9];
    bool cmp1(knight a,knight b)
    {
        if(a.y==b.y)
        return a.x<b.x;
        else return a.y<b.y;
    }
    void Solve1()
    {
        for(int i=1;i<=m;i++)
        {
            if(k[i].y==k[i-1].y)
            wudi[k[i].num]++;
            if(k[i].y==k[i+1].y)
            wudi[k[i].num]++;
        }
    }
    bool cmp2(knight a,knight b)
    {
        if(a.x==b.x) return a.y<b.y;
        else return a.x<b.x;
    }
    void Solve2()
    {
        for(int i=1;i<=m;i++)
        {
            if(k[i].x==k[i-1].x)
            wudi[k[i].num]++;
            if(k[i].x==k[i+1].x)
            wudi[k[i].num]++;
        }
    }
    bool cmp3(knight a,knight b)
    {
        if(a.x+a.y==b.x+b.y)
        return a.x-a.y<b.x-b.y;
        else return a.x+a.y<b.x+b.y;
    }
    void Solve3()
    {
        for(int i=1;i<=m;i++)
        {
            if(k[i].x+k[i].y==k[i-1].x+k[i-1].y)
            wudi[k[i].num]++;
            if(k[i].x+k[i].y==k[i+1].x+k[i+1].y)
            wudi[k[i].num]++;
        }
    }
    bool cmp4(knight a,knight b)
    {
        if(a.x-a.y==b.x-b.y)
        return a.x+a.y<b.x+b.y;
        else return a.x-a.y<b.x-b.y;
    }
    void Solve4()
    {
        for(int i=1;i<=m;i++)
        {
            if(k[i].x-k[i].y==k[i-1].x-k[i-1].y)
            wudi[k[i].num]++;
            if(k[i].x-k[i].y==k[i+1].x-k[i+1].y)
            wudi[k[i].num]++;
        }
    }
    int main(){
        scanf("%d %d",&n,&m);
        for(int i=1;i<=m;i++)
        {
            cin>>k[i].x>>k[i].y;
            k[i].num=i;
        }
        sort(k+1,k+1+m,cmp1);
        Solve1();
        sort(k+1,k+1+m,cmp2);
        Solve2();
        sort(k+1,k+1+m,cmp3);
        Solve3();
        sort(k+1,k+1+m,cmp4);
        Solve4();
        for(int i=1;i<=m;i++)
            num2[wudi[i]]++;
        for(int i=0;i<=8;i++)
            cout<<num2[i]<<" ";
        return 0;
    }

    这份代码当初我写的时候一直爆零,后来发现是n和m搞混了....

    第二题这个题面和那张图真是让人无力吐槽啊.....

    考试的时候我可能有点脑残了,竟然没想到可以用DP写,我只看见了那个的30%的数据,

    显然可知的情况说明整个序列是降序的,此时把除了最大的那个点即第一个点之外的点都

    移一遍即可。

    当∑n<=1000时,我们考虑简化题意,我们可以得到 :最优解不可能把同一个数字移动 次及以上。既然我们可以一次把它移到正确的位置上,那么这个
    数字就可以理解为直接被移走了。
    那么问题就变成了:删除几个数字,使得数列单调递增,所求的是移走数字的最小和。 使得删除的和最小,就意味
    着留下的和最大。
    那么问题就变成了:保留几个单调递增的数字,使得和最大。
    这个直接O(n²)DP就可以解决

    而对于100%的数据,我们考虑优化O(n²)的DP;发现前面更新f[i]时

    当j<i&&a[j]≤a[i]时f[j]可以转移到f[i]
    用线段树维护一个前缀max,每做完一个i,将f[i]加入线段树。
    时间复杂度O(nlogn)。

    代码:

    #include<bits/stdc++.h>
    using namespace std;
    int T,n;
    int ji[1000000+10];
    bool flag,hhh;
    long long uuu,sum,cnt;
    long long b[1000000+10],c[1000000+10];
    map<int,int>q;
    long long f;
    struct super_tree{
        int l,r;
        long long sum;
    }t[4000000+10];
    long long rd()
    {
        long long a=0;
        char s;
        s=getchar();
        while(s<'0'||s>'9')
        s=getchar();
        while(s>='0'&&s<='9')
        {
            a=a*10+s-'0';
            s=getchar();
        }
        return a;
    }
    void build(int p,int l,int r)
    {
        t[p].l=l;
        t[p].r=r;
        t[p].sum=0;
        if(l==r)
            return;
        int mid=(l+r)>>1;
        build(p*2,l,mid);
        build(p*2+1,mid+1,r);
    }
    void change(int p,int l,int r,long long v)
    {
        if(t[p].l>=l&&t[p].r<=r)
        {
            t[p].sum=max(v,t[p].sum);
            return;
        }
        int mid=(t[p].l+t[p].r)>>1;
        if(mid>=l) change(p*2,l,r,v);
        if(mid<r) change(p*2+1,l,r,v);
        t[p].sum=max(t[p*2].sum,t[p*2+1].sum);
    }
    long long query(int p,int l,int r)
    {
        if(t[p].l>=l&&t[p].r<=r)
        {
            return t[p].sum;
        }
        long long ans=0;
        int mid=(t[p].l+t[p].r)>>1;
        if(mid>=l) ans=max(ans,query(p*2,l,r));
        if(mid<r) ans=max(ans,query(p*2+1,l,r));
         return ans;
    }
    void Solve(){
        n=rd();
        for(int i=1;i<=n;i++)
        {
            ji[i]=rd();
            b[i]=ji[i];
            sum+=ji[i];
        }
        cnt=0;
        sort(b+1,b+1+n);
        for(int i=1;i<=n;i++)
        {
            if(i==1||b[i]!=b[i-1])
            q[b[i]]=++cnt;
            else q[b[i]]=cnt;
        }
        for(int i=1;i<=n;i++)
        c[i]=q[ji[i]];
        build(1,1,n);
        for(int i=1;i<=n;i++)
        {
            f=query(1,1,c[i])+ji[i];
            change(1,c[i],c[i],f);
            uuu=max(uuu,f);
        }
        cout<<sum-uuu<<endl;
        for(int i=1;i<=n;i++)
        q[ji[i]]=0;
    }
    int main(){
        T=rd();
        while(T--)
        {
            uuu=0;
            sum=0;
            Solve();
        }
        return 0;
    }

     这就是11号的所有题目,我理解的还比较透彻。只是如果能在考试的的时候写出来就好了。

  • 相关阅读:
    yum管理工具
    ansible简介,简单实用
    ssh服务简介及应用与服务的进程的类型
    keepliave
    NFS
    编译安装redis4.0
    redis多实例和高可用
    NTP时间服务
    ssl简介与openssl的使用
    一些高效学习方法-牛人是怎么来的
  • 原文地址:https://www.cnblogs.com/raochenxing/p/11173852.html
Copyright © 2020-2023  润新知