【洛谷】P1878 舞蹈课(链表+二叉堆)

题意

给定\(n\)个人,每个人有一个舞蹈技术值,相邻且舞蹈技术值相差最小的一对异性会出队,求出队的总次数以及出队的顺序。

思路

首先看到最小值,可以想到用小根堆来实现。

先将每一种可能的出队方案储存到小根堆中。

可以定义一个数组来表示是否出队。如果相邻的舞者有人已经出队,便跳过。

每一次出队以后,就会产生一对新的相邻舞者,可以用数组模拟链表来储存每一位舞者边上的人。出队后便将这一对舞者从链表中删去,同时在答案数组中记录。再判断新产生的一对相邻舞者是否满足出队要求(注意特判边界情况)。

最终得到的便是本题的答案。

代码

#include<cstdio>
#include<queue>
using namespace std;
const int N=2e5+10;
int n,a[N],nex[N],sex[N],pre[N],ans[N][2],cnt;
bool st[N];
char s[N];
int abs(int a){return a>0?a:-a;}
struct node{
	int l,r,dis;
	bool operator <(const node &t)const{ 
	    if(dis==t.dis) return l>t.l; //若有多队舞者差值相同,则最左边的一对优先出队 
	    return dis>t.dis;
	}
};
priority_queue<node>q;
int main()
{
	scanf("%d",&n);
	scanf("%s",s+1);
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&a[i]);
		if(s[i]=='B') sex[i]=1;
		pre[i]=i-1,nex[i]=i+1; //链表初始化 
	}
	for(int i=1;i<n;i++)
	    if((sex[i]^sex[i+1])==1) q.push(node{i,i+1,abs(a[i]-a[i+1])});// 若相邻两人异性,则异或值为1,反之则为0 
	while(q.size())
	{
		node now=q.top();
		q.pop();
		if(!st[now.l]&&!st[now.r]) //都仍未出队 
		{
			st[now.l]=true;//标记出队 
			st[now.r]=true;	
			ans[++cnt][0]=now.l;//记录答案 
			ans[cnt][1]=now.r;
			nex[pre[now.l]]=nex[now.r];//删去出队的舞者 
			pre[nex[now.r]]=pre[now.l];
			if(nex[now.r]>n||pre[now.l]<1) continue; //特判边界 
			if((sex[pre[now.l]]^sex[nex[now.r]])==1) q.push(node{pre[now.l],nex[now.r],abs(a[pre[now.l]]-a[nex[now.r]])});
		    //判断新产生的相邻舞者是否满足出队要求 
		}
	}
	printf("%d\n",cnt);
	for(int i=1;i<=cnt;i++) printf("%d %d\n",ans[i][0],ans[i][1]);
	return 0;
}
posted @ 2021-05-30 18:35  曙诚  阅读(212)  评论(0)    收藏  举报