舞蹈课(dance.cpp/c/pas)

时间:2022-10-20 04:01:25

【问题描述】
有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(*)