舞蹈课(dancinglessons)

舞蹈课(dancinglessons)

洛谷p1878

题目大意

n个人排成一列,给出n个人的性别及a[i]值,每次让a[i]值相差最小的两个异性出列,输出这样的对数及两人的编号。n<=200000。

题解

50分算法

对于第一个问题,只需要输出男生和女生中个数少的那一个,因为最终一定有一方全部出列,不然肯定有男生和女生相邻,就会继续出列。
对于第二个问题,构建一个结构体node,来储存每个人的所有信息。这和约瑟夫问题有异曲同工之妙,我们可以把每个人的位置(编号)作为元素,建立双向链表,于是可以把编号作为node里的下标,知道这个元素的所有信息。链表的好处是可以O(1)时间删除链表中元素,双向的好处是可以方便地找到每个元素的前驱和后继。美中不足的是,每次让最小的一对出列需要扫描整个链表,所以复杂度是O(n^2)。
代码如下。

#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
#define INF 1000000000
struct node
{
    char c;
    int data,pre,next;
}a[200005];
int n;
int main()
{
    cin>>n;
    int g=0,b=0;
    for (int i=1;i<=n;i++) 
    {
        cin>>a[i].c;
        if (a[i].c=='G') g++;
        else b++;
    }
    for (int i=1;i<=n;i++)
    {
        cin>>a[i].data;
        a[i].pre=i-1;
        a[i].next=i+1;
    }
    a[0].next=1;
    cout<<min(g,b)<<'\n';
    for (int i=1;i<=min(g,b);i++)
    {
        int x=a[0].next,y=INF,z;
        while (a[x].next!=n+1)
        {
            int t=a[x].next;
            if (a[x].c==a[t].c) 
            {
                x=a[x].next;
                continue;
            }
            if (abs(a[x].data-a[t].data)<y)
            {
                y=abs(a[x].data-a[t].data);
                z=x;
            }
            x=a[x].next;
        }
        cout<<z<<' '<<a[z].next<<'\n';
        a[a[z].pre].next=a[a[z].next].next;
        a[a[a[z].next].next].pre=a[z].pre;
    }
}
100分算法

双向链表可以保留。考虑如何快速找到最小的那一对。于是想到可以把每一对作为一个元素,放进优先队列里。
如果不会优先队列,可以点这里
我是用node1把每一对打包,放入优先队列里。每次弹出队头,如果这一对中有人已出列,就跳过,不然,他们就出列。出列之后,要看一看两边新组成的这一对是不是异性,如果是,就入队。当然,双向链表还是需要的,不然找不到元素的前驱和后继。
因为优先队列内部是用堆实现的,所以每次操作的复杂度为O(logn),总复杂度为O(nlogn)。
具体实现如下。

#include<iostream>
#include<cstdio>
#include<queue>
#include<cmath>
using namespace std;
int n,b[200005];
struct node {
    char c;
    int data,pre,next;
} a[200005];
struct node1 {
    int x1,x2;
    node1() {}
    node1(int x,int y):x1(x),x2(y) {}
    bool operator <(node1 x) const {
        if (abs(a[x1].data-a[x2].data)!=abs(a[x.x1].data-a[x.x2].data)) return abs(a[x1].data-a[x2].data)>abs(a[x.x1].data-a[x.x2].data);
        return x1>x.x1;
    }
};
priority_queue<node1> p;
int main() {
    cin>>n;
    int g=0;
    for (int i=1; i<=n; i++) {
        cin>>a[i].c;
        if (a[i].c=='G') g++;
    }
    for (int i=1; i<=n; i++) {
        cin>>a[i].data;
        a[i].pre=i-1;
        a[i].next=i+1;
    }
    cout<<min(g,n-g)<<'\n';
    for (int i=1; i<n; i++) {
        if (a[i].c!=a[i+1].c) {
            p.push(node1(i,i+1));
        }
    }
    while (!p.empty()) {
        node1 x;
        x=p.top();
        p.pop();
        if (x.x1==0||x.x2==n+1) continue;
        if (b[x.x1]==1||b[x.x2]==1) continue;
        b[x.x1]=b[x.x2]=1;
        cout<<x.x1<<' '<<x.x2<<'\n';
        if (a[a[x.x1].pre].c!=a[a[x.x2].next].c) p.push(node1(a[x.x1].pre,a[x.x2].next));
        a[a[x.x1].pre].next=a[x.x2].next;
        a[a[x.x2].next].pre=a[x.x1].pre;
    }
}

补充

关于结构体,还想再说一点。第2个程序第14行的是构造函数,可以把2个数转换为node1类型(请看第36行)。第13行本来可以不用写的,不过有了构造函数就一定要有这个函数,不然将无法定义node1类型的数(删去第13行后,第40行就写不了了)。

总结

在结构体里重载<的时候,要注意优先队列是个大根堆,若想要小的在前面,return后面就应该写>,跟它反着来。
这是我写的第一篇博客,要是有什么不足之处,望大家指出。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值