P1878舞蹈课

· · 个人记录

思路

这道题难就难在体面容易理解,但代码写起来很棘手(我是说手写堆),先简单看一下体面,抓取重点:

1.当这一排中至少有一对相邻的异性

2.舞蹈技术相差最小的那一对出列

3.最左边的那一对先出列

4.队伍中的空白按原顺序补上

描述中说模拟,确实是模拟,怎么模拟呢? 过程如下:

1.先建一个Bool数组(或者Char数组),用于存放性别

2.输入舞蹈技术值,同时将一对异性入堆(入堆的是舞蹈技术相差)

3.取出堆顶那一对人(理由:小根堆中根最小)

4.保证此时堆顶那一对人不是已经出堆的人

5.讲不上后的队伍再次取出一对异性入堆

6.重复3、4、5,直到堆为空

7.输出

代码

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cmath>
const int maxn = 200010;
struct node
{
    int value,l,r;
 friend bool operator > (node aa,node bb){return aa.value > bb.value || (aa.value == bb.value && aa.l > bb.l);} 
    //重载运算符,根据题意,技术值差的先出堆,如果值相同,则先左(即l越小) 
}heap[maxn],tmp;
void swap(node &a,node &b){tmp = a,a = b,b = tmp;}
int n,a[maxn],heap_size,tot,tl[maxn],tr[maxn]; 
bool b[maxn],f[maxn];
void put(int value,int l,int r){
    int now = ++heap_size,next;
    heap[heap_size].value = value,heap[heap_size].l = l,heap[heap_size].r = r;
    while(now > 1){
        next = now >> 1;
        if(heap[now] > heap[next]) return;
        swap(heap[now],heap[next]);
        now = next;
    } 
}
void get(){
    int now = 1,next;
    heap[1].value = heap[heap_size].value,heap[1].l = heap[heap_size].l,heap[1].r = heap[heap_size--].r;
    while(now << 1 <= heap_size){
        next = now << 1;
        if(next < heap_size && heap[next] > heap[next + 1]) next++;
        if(heap[next] > heap[now]) return;
        swap(heap[now],heap[next]);
        now = next;
    }
}
//日常堆操作
int main(){
    scanf("%d\n",&n);
    for(int i = 0;i < n;i++){
        char ch = getchar();
        f[i] = ch == 'B';//若这个人他是'B'置f[i]为true,反之亦反 
    }//巧妙地输入情况 
    for(int i = 0;i < n;i++){
        scanf("%d",&a[i]);//输入舞蹈技术 
        int j = i - 1;//置j为i前面那个人 
        if(i && f[i] ^ f[j]) put(abs(a[i] - a[j]),j,i);
        //如果i不是指向第一个人,且f[i] != f[j],将舞蹈技术值差和两个人的下标入堆 
    }
    while(heap_size){
        int l = heap[1].l,r = heap[1].r,ll,rr;//当前最小的那一对出堆 
        b[l] = b[r] = true;//置他们为出堆状态 
        tl[++tot] = l + 1,tr[tot] = r + 1;//将他们的下标存入数组,tot+1 
        do{
            get();//出堆操作 
            if(!heap_size) break;//如果堆空就退出 
            ll = heap[1].l,rr = heap[1].r;
        }while(b[ll] || b[rr]);
        //此操作维护该堆直至堆顶不为已出堆的元素 
        while(l >= 0 && b[l]) l--;
        while(r < n && b[r]) r++;
        //寻找空白两边的元素,相当于按原顺序补上 
        if(l >= 0 && r < n && f[l] ^ f[r]) put(abs(a[l] - a[r]),l,r);//将其入堆 
    }
    printf("%d",tot);
    for(int i = 1;i <= tot;i++) printf("\n%d %d",tl[i],tr[i]);
    //输出答案 
    return 0;
}