/*
Name: P1878_舞蹈课
Copyright:
Author: 巧若拙
Date: 01-03-17 07:42
Description:
有n个人参加一个舞蹈课。每个人的舞蹈技术由整数来决定。在舞蹈课的开始,他们从左到右站成一排。
当这一排中至少有一对相邻的异性时,舞蹈技术相差最小的那一对会出列并开始跳舞。
如果不止一对,那么最左边的那一对出列。一对异性出列之后,队伍中的空白按原顺序补上
(即:若队伍为ABCD,那么BC出列之后队伍变为AD)。舞蹈技术相差最小即是ai的绝对值最小。
任务是模拟以上过程,确定跳舞的配对及顺序。
输入输出格式
输入格式:
第一行为正整数n(1<=n<=2*10^5):队伍中的人数。下一行包含n个字符B或者G,B代表男,G代表女。
下一行为n个整数ai(ai<=10^7)。所有信息按照从左到右的顺序给出。在50%的数据中,n<=200。
输出格式:
第一行:出列的总对数k。接下来输出k行,每行是两个整数。按跳舞顺序输出,两个整数代表这一对舞伴的编号
(按输入顺序从左往右1至n编号)。请先输出较小的整数,再输出较大的整数。
输入输出样例
输入样例#1:
4
BGBG
4 2 4 3
输出样例#1:
2
3 4
1 2
算法思路:
设置一个Node类存储每对舞伴的左右舞者下标l,r和舞技差值w。
一开始读取所有相邻的舞伴信息,并插入到最小堆。
然后执行以下循环体,直至最小堆为空:
记录堆顶元素的舞者下标(当前配对舞者),标注当前舞者已配对,并删除堆顶元素,
然后删除所有涉及已配对舞者的堆顶元素,确保下次提取的顶点是满足条件的配对。
接下来跳过所有已配对舞者,用其两侧的舞者填补空白,查看是否能形成新的配对,
若能配对,则生成新结点,并插入到最小堆。
注意:题目要求“如果不止一对,那么最左边的那一对出列”,所以建立最小堆的时候,
不仅仅要看w值,还要比较左侧舞者的编号,编号小的靠前。
所以重载>运算符的时候要同时比较w和l的值。
分点信息(鼠标移到方块上有详细信息)
#1
AC
3ms/1589kB
#2
AC
5ms/18207kB
#3
AC
3ms/1593kB
#4
AC
4ms/1589kB
#5
AC
2ms/18207kB
#6
AC
731ms/4039kB
#7
AC
676ms/3679kB
#8
AC
721ms/3996kB
#9
AC
749ms/3687kB
#10
AC
409ms/20457kB
*/
#include<iostream>
#include<algorithm>
#include<math.h>
#include<queue>
using namespace std;
class Node
{
public:
// void PrintNode() {cout << l+1 << " " << r+1 << " --" << w << endl;}
void SetNode(int p1, int p2, int v) {l = p1; r = p2; w = v;}
int GetL() {return l;}
int GetR() {return r;}
int GetW() {return w;}
friend bool operator < (const Node &op1, const Node &op2)
{
return op1.w < op2.w || (op1.w == op2.w && op1.l < op2.l);
}
private:
int l, r; //分别表示左右两个舞伴的下标
int w;
};
template <typename type> class MinHeap
{
public:
MinHeap(int maxSize); //创建一个容量为maxSize的空堆
~MinHeap() {delete []heap;} //析构函数
const type & top() {return heap[0];} //返回堆顶的最小元素
bool empty(){return size == 0;} //判断是否为空堆
bool push(const type &x); //将x插入到最小堆
bool pop(); //删除堆顶的最小元素
private:
type *heap; //存放堆的元素的数组
int capacity; //堆的容量
int size; //堆的长度,即当前元素个数
void FilterDown(int i); //从下标i到m自顶向下进行调整成为最小堆
void FilterUp(int i); //从下标i到0自底向上进行调整成为最小堆
};
template <typename type> MinHeap<type>::MinHeap(int maxSize)
{
capacity = maxSize;
heap = new type[capacity];
size = 0;
}
template <typename type> void MinHeap<type>::FilterDown(int i) //从下标i到堆的尾部自顶向下进行调整成为最小堆
{
type t = heap[i]; //保存heap[i]的值以备放到适合的位置
int child = i * 2 + 1; //指向左孩子
while (child < size) //有左孩子
{
if (child+1 < size && heap[child+1] < heap[child]) //有右孩子,且右孩子更小些,定位其右孩子
child++;
if (heap[child] < t)//用较小值覆盖其父亲结点的值,即将空位下滤到新的位置
{
heap[i] = heap[child];
i = child;
child = i * 2 + 1;
}
else
break;
}
heap[i] = t;
}
template <typename type> void MinHeap<type>::FilterUp(int i) //从下标i到0自底向上进行调整成为最小堆
{
type t = heap[i];
while (i > 0 && t < heap[i/2]) //若比父亲结点小,则用父亲结点的值覆盖该结点,即将空位上滤到新的位置
{
heap[i] = heap[i/2];
i /= 2;
}
heap[i] = t;
}
template <typename type> bool MinHeap<type>::push(const type &x) //将x插入到最小堆
{
//从尾部插入并向上调整成最小堆,然后长度增1
heap[size++] = x;
FilterUp(size-1);
return true;
}
template <typename type> bool MinHeap<type>::pop() //删除堆顶的最小元素
{
heap[0] = heap[--size];//用尾部元素覆盖顶部元素,然后长度减1
FilterDown(0); //顶部元素向下调整成最小堆
return true;
}
int main()
{
Node t;
const int size = 200000;
MinHeap<Node> H(size);
char S[size];
int A[size];
int n;
cin >> n;
for (int i=0; i<n; i++)
{
cin >> S[i];
}
for (int i=0; i<n; i++)
{
cin >> A[i];
}
for (int i=1; i<n; i++)//不同性别的都入队
{
if (S[i] != S[i-1])
{
t.SetNode(i-1, i, abs(A[i]-A[i-1]));
H.push(t);
}
}
bool F[size] = {0}; //标注是否已经配对成功
int P[size/2][2]; //存储配对成功的舞者编号
int s = 0;
int l, r, p1, p2; //分别存储左右舞伴的下标
while (!H.empty())
{
t = H.top();
l = t.GetL();
r = t.GetR();
F[l] = F[r] = true; //标注该对舞伴已配对
P[s][0] = l + 1; //记录配对舞伴编号(下标+1)
P[s++][1] = r + 1;
do //除去重复配对的顶点,确保下次提取的顶点是满足条件的配对
{
H.pop();
if (H.empty())
break;
t = H.top();
p1 = t.GetL();
p2 = t.GetR();
} while(F[p1] || F[p2]);
//跳过已经配对的舞者
while(l >= 0 && F[l])
{
l--;
}
while(r < n && F[r])
{
r++;
}
if (l>=0 && r<n && S[l] != S[r])//有缘千里来相会
{
t.SetNode(l, r, abs(A[l]-A[r]));
H.push(t);
}
}
cout << s << endl;
for (int i=0; i<s; i++)
{
cout << P[i][0] << " " << P[i][1] << endl;
}
system("pause");
return 0;
}
洛谷在线测试P1878_舞蹈课
最新推荐文章于 2024-03-03 14:24:22 发布