题解 P4379 【[USACO18OPEN]Lemonade Line】

不敢快速排序又想要快排的速度,还不用STL的小伙伴们看这里!
小金羊终于学会了堆排以外的另外的一种排序 (打个题解巩固一下)

归并排序(mergesort):

时间复杂度和快排一样的优秀。
先说归并排序的实现:
首先我们一样的是要进行分解,以达到\(O(logn)\)の时间复杂度。
然后我们需要了解一下它的思想:

如果两个序列的顺序已经排好,于是我们合并两个序列。

emmm...由于分解到最后只有一个元素,一定满足性质。
然后合并?玄学?这咋合并?
其实比较简单。
我们取两个指针,分别指向分解的序列的两个头部,然后比较指针指向的两个元素,满足的就放进我们的辅助空间,直到有一个序列已经用完。
所以如果剩下的序列如何处置?
刚刚说了,两个序列都是有序的,如果剩下的都不满足比原先的元素满足性质(大或者小,或者结构体之类的),那么剩下的就依次都放进去就好了。
没看懂?模拟一下。
首先序列w[10086],表示奶牛想等的时间,假设6头,序列情况如下:
(假设从小到大的顺序

//一开始
int w={6,2,3,4,5,1};
//分解第一次
w1={6,2,3},w2={4,5,1};
//分解第二次
w11={6}/*这个好了*/,w12={2,3},w21={4}/*这个也好了*/,w22={5,1};
//分解最后一次,已经分解完毕的不显示
w121={2},w122={3},w221={5},w222={1};
//所以现在序列是
w11={6},w121={2},w122={3},w21={4},w221={5},w222={1};
//接下来合并最后分解的那一拨
w11={6},w12_ok={2,3},w21={4},w22_ok={1/*来自w222*/,5};
//continue the merge
w1_ok={2,3,6/*比较到6剩下了,就放进来*/},;
w2_ok={1/*来自w22_ok*/,4/*来自w21*/,5/*from w22_ok*/};
//finally
w_ok={1/*from w2_ok*/,2/*from w1_ok...省略剩下的注释*/,3,4,5,6};

这就是归并排序的步骤。看看代码?
(仅仅是从小到大的mergesort样例模板代码)Code:

void mergesort(int l,int r)
{
    if (l==r)return;//已经只剩一个元素
    int mid=(l+r)>>1;//中部元素
    mergesort(l,mid);//分解左边
    mergesort(mid+1,r);//分解右边
    int s2[r-l+1]/*辅助空间*/,p1=l,p2=mid+1,p3=1;
    while (p1<=mid&&p2<=r)//没分解完
    {
        if (w[p1]<=w[p2])s2[p3++]=w[p1++];
        else s2[p3++]=w[p2++];//以上两行是把较小的放进辅助空间
    }
    while (p1<=mid)s2[p3++]=w[p1++];//若有剩余,放进去
    while (p2<=r)s2[p3++]=w[p2++];//若有剩余,放进去
    p3=l;
    for (register int i=1;i<=r-l+1;i++)
        w[p3++]=s2[i];//再把排好的序列覆盖回去
}

看看这个题:

贪心策略:

先把最愿意排队的奶牛放前面,这样最不愿意排队的奶牛都吃草去了,保证队伍最小。
上代码:

#include <iostream>
#include <cstdio>
#include <cstring>

using namespace std;
int w[100001],num,cnt=0;
void mergesort(int l,int r)
{
    if (l==r)return;//已经只剩一个元素
    int mid=(l+r)>>1;//中部元素
    mergesort(l,mid);//分解左边
    mergesort(mid+1,r);//分解右边
    int s2[r-l+1]/*辅助空间*/,p1=l,p2=mid+1,p3=1;
    while (p1<=mid&&p2<=r)//没分解完
    {
        if (w[p1]>=w[p2])s2[p3++]=w[p1++];
        else s2[p3++]=w[p2++];//以上两行是把较小的放进辅助空间
    }
    while (p1<=mid)s2[p3++]=w[p1++];//若有剩余,放进去
    while (p2<=r)s2[p3++]=w[p2++];//若有剩余,放进去
    p3=l;
    for (register int i=1;i<=r-l+1;i++)
        w[p3++]=s2[i];//再把排好的序列覆盖回去
}
int main()
{
    scanf("%d",&num);
    for (register int i=1;i<=num;i++)
        scanf("%d",&w[i]);
    mergesort(1,num);
    for (register int i=1;i<=num;i++)
    {
        if (w[i]<cnt)break;
        cnt++;
	}
	printf("%d",cnt);
    return 0;
}

最后提醒大家一点:

归并排序的辅助空间需求特别大,如是特别要求或者数据范围特别大,请小心使用。

posted @ 2019-02-17 17:57  小金羊  阅读(195)  评论(0编辑  收藏  举报