zoukankan      html  css  js  c++  java
  • Noip模拟考试6:解题报告

    Peter喜欢玩数组。NOIP这天,他从Jason手里得到了大小为n的一个正整数
    数组。
    Peter求出了这个数组的所有子段和,并将这n(n+1)/2个数降序排序,他想
    知道前k个数是什么。

    不难想到,从最大字段和向下更新。

    用set实现

    介绍一下set:用法与priority_queue基本相同,区别是,set有自动去重,而priority_queue将所有元素放入。

    然后通过三元组,分别记录权值,左端与右端。

    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<set>
    #define st first
    #define se second
    #define ll long long
    #define mp(a,b) make_pair(a,b)
    #define pr pair< ll,pair<int,int> > 
    using namespace std;
    set<pr>s;
    ll sum;
    int a[100010];
    int main()
    {
        //freopen("ksum.in","r",stdin);
        //freopen("ksum.out","w",stdout);
        int n,k;
        scanf("%d%d",&n,&k);
        for(int i = 1 ; i <= n ; ++i)
        {
            scanf("%d",&a[i]);
            sum+=a[i];
        }
        s.insert(mp(sum,mp(1,n)));
        while(k--)
        {
            pr now=*s.rbegin();
            printf("%I64d ",now.st);
            s.erase(now);
            int l=now.se.st;
            int r=now.se.se;
            if(l!=r)
            {
                s.insert(mp(now.st-a[l],mp(l+1,r)));
                s.insert(mp(now.st-a[r],mp(l,r-1)));
            }
        }
        return 0;
    }

    2. . 奇袭
    (raid.cppcpas)
    【问题描述】
    由于各种原因,桐人现在被困在Under World(以下简称UW)中,而UW马上
    要迎来最终的压力测试——魔界入侵。
    唯一一个神一般存在的Administrator被消灭了,靠原本的整合骑士的力量
    是远远不够的。所以爱丽丝动员了UW全体人民,与整合骑士一起抗击魔族。
    在UW的驻地可以隐约看见魔族军队的大本营。整合骑士们打算在魔族入侵
    前发动一次奇袭,袭击魔族大本营!
    为了降低风险,爱丽丝找到了你,一名优秀斥候,希望你能在奇袭前对魔
    族大本营进行侦查,并计算出袭击的难度。
    经过侦查,你绘制出了魔族大本营的地图,然后发现,魔族大本营是一个N
    ×N的网格图,一共有N支军队驻扎在一些网格中( 不会有两只军队驻扎在一起)。
    在大本营中,每有一个k×k(1≤k≤N)的子网格图包含恰好k支军队,我们
    袭击的难度就会增加1点。
    现在请你根据绘制出的地图,告诉爱丽丝这次的袭击行动难度有多大。
    【输入格式】
    第一行,一个正整数N,表示网格图的大小以及军队数量。
    接下来N行,每行两个整数,Xi,Yi,表示第i支军队的坐标。
    保证每一行和每一列都恰有一只军队, , 即每一个 Xi 和每一个 Yi 都是不一样
    的。
    【输出格式】
    一行,一个整数表示袭击的难度。

    样例输入:

    5
    1 1
    3 2
    2 4
    5 5
    4 3

    样例输出:

    10

    解题:

    重要信息:保证每一行和每一列都恰有一只军队, , 即每一个 Xi 和每一个 Yi 都是不一样
    的。

    由此可将二维首先优化至一维。

    样例则分别为 :1 4 2 3 5

    观察发现,若某一区间最大值减最小值的差等于(这段区间的长度-1)那么该段区间可选。

    若左右端点分别为i,j,那么 满足的条件为 i-j+1=max[i,j]-min[i,j]。

    分治算法:

    在每次solve 中,会出现两种情况:

    1、最大值与最小值都出现在同一端(l<x<mid或者mid+1<x<r),只需要预处理max与min.O(n)处理即可

    2、最大值与最小值出现不同端,处理起来就比较复杂了。

    首先,lmax[i]表示从mid到i点的最大值 ,同理rmax[i]表示从mid+1到i点的最大值

    lmin与rmin同理。

    分情况考虑:

    1、[l,mid],从l到mid 过程中lmax是下降序列,lmin 为上升序列。

    2、[mid+1,r] 从mid+1到r 的过程中rmax 是上升序列,rmin 是下降序列。

    由上面公式,设最大值出现在右端 则 j-i+1=max[j]-min[i]+1;

    -->max[j]-j=min[i]-i;

    还有两个条件:max[j]>max[i],min[i]<min[j]。

    那么对max[j]-j进行预处理 ,再用min[i]-i进行对应,O(n)处理。

    在代码里再进行解释吧。

    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #define ll long long
    using namespace std;
    const int maxn = 50010;
    int TAX[maxn<<1];//设置一个桶 ,用来存放max[j]-j预处理得到的值。
    int *tax=&(TAX[maxn]),q[maxn];//max[j]-j会出现负值,所以将数组开到0以下。
    int Lmax[maxn],Lmin[maxn],Rmax[maxn],Rmin[maxn];
    int maxx = - (1<<30);
    int minn = 1 << 30;
    ll calc(int l,int r,int mid)
    {
        ll ret=0;
        Lmax[mid]=Lmin[mid]=q[mid];
        Rmax[mid+1]=Rmin[mid+1]=q[mid+1];
        for(int i = mid-1 ; i >= l ;--i)
        {
            Lmax[i]=max(Lmax[i+1],q[i]);
            Lmin[i]=min(Lmin[i+1],q[i]);
        }
        for(int i = mid+2 ; i <= r ;++i)
        {
            Rmax[i]=max(Rmax[i-1],q[i]);
            Rmin[i]=min(Rmin[i-1],q[i]);
        }//预处理max和min
        for(int i = l ; i <= mid ; ++i)
        {
            int j=i+Lmax[i]-Lmin[i];
            if(j <= r&&j>mid&&Rmax[j]<Lmax[i]&&Rmin[j]>Lmin[i])ret++;
        }//最大值与最小值出现在同一端的情况
        int q=mid,p=mid+1;
        while(q<r&&Rmin[q+1]>Lmin[l])q++,tax[Rmax[q]-q]++;//如果右端下一个位置仍然大于最左端的min值,那么当前值入桶
        while(p<=r&&Rmax[p]<Lmax[l])tax[Rmax[p]-p]--,p++;//如果当前位置小于最左端max值,那么当前值出桶
        for(int i = l ; i <= mid ; ++i)//通过min[i]-i来对应max[j]-j
        {
            while(q>mid &&Rmin[q]<Lmin[i])tax[Rmax[q]-q]--,q--;//如果当前q位置Min小于i位置min,则该情况不合法,出桶
            while(p>mid+1&&Rmax[p-1]>Lmax[i])p--,tax[Rmax[p]-p]++;//如果下一位置仍然合法,那么将下一位置入桶。
            ret+=max(0,tax[Lmin[i]-i]);
        }//像是一个动态右移的过程,先预处理右端所有情况针对左端最左值得情况,然后通过左端右移,来实现桶的入与出。
        for (int i = mid + 1 ; i <= r ; i ++ )//清空
            tax[Rmax[i]-i] = 0 ;
        return ret;
    }
    ll Solve(int l,int r)//分治部分
    {
        ll ret;
        if(l==r)return 1ll;
        int m=(l+r)>>1;
        ret=Solve(l,m)+Solve(m+1,r);
        ret+=calc(l,r,m);
        reverse(q+l,q+1+r);//将队列反置,下面计算max与min出现在另一端,或者max出现在左端,min出现在右端的情况。
        if((r-l+1)%2)m--;//自己手推一推就明白啦~
        ret+=calc(l,r,m);
        reverse(q+l,q+1+r);
        return ret;
    }
    int main()
    {
        freopen( "raid.in" , "r" , stdin ) ;
        freopen( "raid.out" , "w" , stdout ) ;
        int n,x,y;
        scanf("%d",&n);
        for(int i = 1 ; i <= n ; ++i)
        {
            scanf("%d%d",&x,&y);
            q[x]=y;
        }
        printf("%I64d
    ",Solve(1,n));
        return 0;
    }

    很有意思的一道分治题,不过对于我这种蒟蒻到不行的蒟蒻来说,只能依靠题解苟活才会写。

    3.十五数码
    (fifteen.cpp/c/pas)

    给出起始顺序,要求通过 0 的移动(与上下左右交换),排成以下顺序:
    1 2 3 4
    5 6 7 8
    9 10 11 12
    13 14 15 0

    输出最少移动次数。如果无解输出 No。

    这个单独介绍一篇吧,看一下这个:

  • 相关阅读:
    0031 Java学习笔记-梁勇著《Java语言程序设计-基础篇 第十版》英语单词
    0030 Java学习笔记-面向对象-垃圾回收、(强、软、弱、虚)引用
    0029 Java学习笔记-面向对象-枚举类
    0028 Java学习笔记-面向对象-Lambda表达式
    0027 Java学习笔记-面向对象-(非静态、静态、局部、匿名)内部类
    0026 Java学习笔记-面向对象-抽象类、接口
    0025 Java学习笔记-面向对象-final修饰符、不可变类
    0024 Java学习笔记-面向对象-包装类、对象的比较、String常量池问题
    0023 Java学习笔记-面向对象-初始化代码块
    0022 Java学习笔记-面向对象-继承、多态、组合
  • 原文地址:https://www.cnblogs.com/fujudge/p/7397974.html
Copyright © 2011-2022 走看看