洛谷在线测试P1878_舞蹈课

/*
  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;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值