【问题描述】
有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
【样例输入2】
4
BGBB
1 1 2 3
【样例输出2】
1
1 2
【来源】
Noi导刊
题解:
(*)
Done By NOI导刊:
双向链表+堆
堆中存储:相邻异性的编号及技术差值。
按照技术差值为第一关键字,编号为第二关键字排序
双向链表用来快速寻找i号左边的人和右边的人。
算法流程:
1.将所有相邻的异性加入堆;
2.当堆中还有元素时,取出并删除堆顶,否则结束;
3.如果堆顶的两个人(x,y)有一个已经出列或者都出列了,跳到2
4.将x和y标记为已出列并加入答案,检查x左边和y右边的人是否为异性,如果是就加入堆;
5.将x和y从双向链表中删除,跳到2.
(*)
今天发现当初加了很多注释,所以发上来..
1 Var 2 i,j:longint; //i,j 主程序中循环变量 3 k,n:longint; //k:配对成功数量 n:舞蹈课人数 4 x,y:longint; //临时左右配对编号 5 DP:longint; //总可能配对方案数 6 a,b,c:Array[0..301001] of longint; 7 //a:技术差值 b:技术值 c:判断队伍中人是否已配对成功 8 DA,DC:array[0..301001,0..2] of longint; 9 //DA:成对舞伴编号 DC:配对左右编号 10 s:Array[0..301001] of Char; //s:存放性别 11 xx:BooLean; //判断方案是否产生(即是否配对成功).. 12 ProceDure Sort(h,n:longint); 13 //按照技术差值为第一关键字,编号为第二关键字排序 14 Var 15 i,j,Tt,Head,Tail,z:longint; 16 Begin 17 i:=h; //i,第一指针,前 18 j:=i shl 1; //j,第二指针,后 19 Tt:=a[i]; //Tt为初始最小差值 20 Head:=DC[i,0]; //配对左侧位置 21 Tail:=DC[i,1]; //配对右侧位置 22 //z:=i; 23 While j<=n do 24 Begin 25 if ((j<n) And ( (a[j]>a[j+1]) Or ( (a[j]=a[j+1]) And (DC[j,0]>DC[j+1,0]) ) ) ) Then Inc(j); 26 //如果j还小于n并且第j个大于第j+1个或者第j个等于第j+1个但第j个在第j+1个的右边,那么指针j++ 27 //即如果当前指针在链表中,且右移后得到的差值及配对方案仍由于当前,指针右移 28 if ((Tt<a[j]) Or ( (Tt=a[j]) And (Head<DC[j,0]) ) ) then Break; 29 //如果第j个比Tt大或者Tt跟第j个一样大但头指针比第j个的头靠左,那么退出循环 30 //即若当前指针所指的差值比已知最小值大,或虽相等配对方案在队伍更右侧,排序结束,退出 31 a[i]:=a[j]; 32 DC[i,0]:=DC[j,0]; 33 DC[i,1]:=DC[j,1]; 34 //更新a,DC数组为当前最优解 35 //由上两个if判断可得,当前j指针所指的差值及方案,比原方案的要优 36 i:=j; 37 j:=j Shl 1; 38 //更新i, j两指针 39 //查看由原来j开始到j*2++段中是否仍能找到更优解 40 End; 41 a[i]:=Tt; 42 DC[i,0]:=Head; 43 DC[i,1]:=Tail; 44 //由于While循环中的判断,当前的Tt已经是剩余最小差值,DC得到了当前最小差值的最靠左配对 45 End; 46 Begin 47 Readln(n); 48 For i:=1 to n do 49 Read(s[i]); 50 Readln; 51 For i:=1 to n do 52 Begin 53 Read(b[i]); 54 if (i<>1) And (s[i]<>s[i-1]) Then //若异性,则 55 Begin 56 Inc(DP); //可配对数量 57 a[DP]:=Abs(b[i]-b[i-1]); //配对技术差值 58 DC[DP,0]:=i-1; //配对左标号 59 DC[DP,1]:=i; //配对右标号 60 End; 61 End; 62 //初始化,找到相邻可配对,得到其技术差值,存入a数组,DC[x,0]表示前节点,DC[x,1]表示后节点 63 //堆中存储:相邻异性的编号及技术差值。 64 For i:=DP Shr 1 downto 1 do Sort(i,DP);//1对=2人... 65 For i:=1 to n shr 1 do //1对=2人.... 66 Begin 67 xx:=False; //判断第i对是否出列.. 68 While Not xx do 69 Begin 70 x:=DC[1,0]; 71 y:=DC[1,1]; //当前配对左右编号分别赋入x,y 72 if (c[x]=0) and (c[y]=0) Then //若当前所指的配对方案两人均未出列,选择该方案,2人出列 73 Begin 74 c[x]:=1; 75 c[y]:=1; //两人出列 76 DA[i,0]:=x; 77 DA[i,1]:=y; //Answer赋值 78 xx:=True; //第i对已选择 79 While (c[x]=1) And (x>=0) do Dec(x); //更新左指针 80 While (c[y]=1) And (y<=n+1) do Inc(y); //更新右指针 81 if (x>0) And (y<=n) And (s[x]<>s[y]) Then //若当前指针合法,且指向两个异性,则 82 Begin 83 a[1]:=Abs(b[x]-b[y]); //更新为当前两人差值 84 DC[1,0]:=x; 85 DC[1,1]:=y; //首初始化为当前两人标号 86 Sort(1,DP); //归队,排序 87 End; 88 End 89 Else //若当前所指“最优”配对方案中,2人至少1人出列 90 Begin 91 a[1]:=a[DP]; //更新为最末(最可能没出队的...) 92 DC[1,0]:=DC[DP,0]; 93 DC[1,1]:=DC[DP,1]; //标号随之更新.. 94 Dec(DP); //最终可能方案数 DP-- 95 Sort(1,DP); //重新排序 96 End; 97 if DP=0 Then Break; //若已无方案,退出 98 End; 99 if DP=0 Then Break; //若已无方案,退出 100 End; 101 k:=0; //最终配对数量初始化 102 For j:=1 to i do //由上,最多有i对.. 103 if (DA[j,0]<>0) then Inc(k); //若DA中存在信息,说明有配对存在 104 Writeln(k); //输出最终配对数量 105 For i:=1 to k do 106 Writeln(DA[i,0],' ',DA[i,1]); //输出成对舞伴编号... 107 End. 108 (*)程序中注释 Written By Catch-22.S.In(*)