舞蹈课

舞蹈课

【问题描述】
有n 个人参加一个舞蹈课。每个人的舞蹈技术由整数来决定。在舞蹈课的开始,他们从
左到右站成一排。当这一排中至少有一对相邻的异性时,舞蹈技术相差最小的那一对会出猎并开
始跳舞。如果不止一对,那么最左边的那一对出列。一对异性出列之后,队伍中的空白按原顺序补上
(即:若队伍为ABCD,那么BC 出列之后队伍变成AD)。舞蹈技术相差最小即是|ai|最小。
你的任务是,模拟以上过程,确定跳舞的配对及顺序。
【输入】
第一行为正整数n(1<=n<=2*10^5),表示队伍中的人数。下一行包含n 各字符B 或G,表示男
孩还是女孩。下一行为n 个整数ai(ai<=10^7)。所有信息按照从左到右的顺序给出。在50%的数
据中,n<=200。
【输出】
第一行:出列的总对数k。接下来输出k 行,每行是两个整数,按跳舞顺序输出。两个整数代表
这一对舞伴的编号(按输入顺序从左往右1 至n 编号)。请先输出较小的整数,再输出较大的整数。

作为今天模拟赛的第三题 一看还觉得是数论啊什么的
后来发现是一道“普及+/提高”的模拟题
把所有相邻的异性加入队列 (优先队列 从大往小排 要注意字典序排序)
往后扫看是否能凑对
vist存是否配对过,while配对过直接跳过,直到两端或者不为配对过的就行了,还有就可以存两端位置,直接更新,有点像链的那种存储。
优先队列判定加个如果相等就取左边小的,很明显可以证明不可能存在交叉,所以一定是对的。

#include<iostream>
#include<cstdio>
#include<algorithm> 
#include<queue>
#include<cmath>
using namespace std;
struct node
{
    int x,y,c;
    bool operator<(const node &p)const{
        if(p.c==c)
        {
            return p.x<x;
         } 
        return p.c<c;
    }
 };
priority_queue<node> line;
int cnt=0;
struct NODE
{
    int x,y;
} partner[100005];
int n,a[200005];
bool vis[200005];
char c[200005];
int main()
{
    freopen("dancinglessons.in","r",stdin);
    freopen("dancinglessons.out","w",stdout);
    scanf("%d",&n);
    scanf("%s",c);
    scanf("%d",&a[1]);
    for(int i=2;i<=n;i++)
    {
        scanf("%d",&a[i]);
        node t;
        if(c[i-1]!=c[i-2])
          {
            t.x=i-1;
            t.y=i;
            t.c=fabs(a[i]-a[i-1]);//记录间距 
            line.push(t);
          }    
    }
    bool flag;
    while(!line.empty())
    {
        flag=0;
        node t=line.top();
        line.pop();
        if(vis[t.x]||vis[t.y]) continue;
        partner[++cnt].x=t.x;
        vis[t.x]=true;
        partner[cnt].y=t.y;
        vis[t.y]=true;
        while(vis[t.x-1])
        {
            if(t.x<=2)
            {
                flag=1;
                break;
            }
            t.x--;
        }
        if(flag==1)
        continue;
        while(vis[t.y+1])
        {
            if(t.y>=n-1)
            {
                flag=1;
                break;
            }
            t.y++;
        }
        if(flag==1)
          {
            continue;
          }
        if(c[t.x-2]!=c[t.y]&&t.x>1&&t.y<n)
          {
            t.x=t.x-1;
            t.y=t.y+1;
            t.c=fabs(a[t.x]-a[t.y]);
            line.push(t);
          }    
    }
    printf("%d\n",cnt);
    for(int i=1;i<=cnt;i++)
    {
        printf("%d %d\n",partner[i].x,partner[i].y);
     } 
}

前面说了储存有点链的味道
所以也可以用堆+双向链表
将所有相邻异性加入堆(大根)
当队中还有元素时,取出并删除堆顶,否则结束
如果堆顶两个人出列了继续循环
将a和b标记为已出列检查a左边和b右边是否为异性如果是加入堆
将a和b从双向链表中删除

#include<stdio.h>
#include<stdlib.h>

#define abs(a) (a>0 ? (a):-(a))
int i,j,k,n,m,a[301000],top,b[301000],c[301000];
int f1[301000],f2[301000];
int ans[301000][2];
char s[301000];
void work(int h,int n)
{
     int i=h,j=i*2,tmp=a[i],tmpf1=f1[i],tmpf2=f2[i],z=i;
     while (j<=n){
           //printf("%d %d %d %d\n",i,j,a[i],a[j]);
           if (j<n&&(a[j]>a[j+1]||(a[j]==a[j+1]&&f1[j]>f1[j+1]))) j++;
           if (tmp<a[j]||(tmp==a[j]&&tmpf1<f1[j]))
              break;
           a[i]=a[j];
           f1[i]=f1[j];
           f2[i]=f2[j];
           i=j;
           j*=2;
           }
     a[i]=tmp;
     f1[i]=tmpf1;
     f2[i]=tmpf2;
}
int main()
{
    freopen("dancinglessons.in","r",stdin);
    freopen("dancinglessons.out","w",stdout);
    scanf("%d",&n);
    getchar();
    for (i=1;i<=n;i++)
        scanf("%c",&s[i]);
    for (i=1;i<=n;i++){
        scanf("%d",&b[i]);
        if (i!=1&&s[i]!=s[i-1])
           top++,a[top]=abs(b[i]-b[i-1]),f1[top]=i-1,f2[top]=i;
           }

    for (i=top/2;i>=1;i--)
        work(i,top);

    for (i=1;i<=n/2;i++){
        int flag=0;
        while (!flag){
              int x,y;
              x=f1[1],y=f2[1];
              if (c[x]==0&&c[y]==0){
                 c[x]=1,c[y]=1;
                 ans[i][0]=x,ans[i][1]=y;
                 flag=1;
                 while (c[x]==1&&x>=0) x--;
                 while (c[y]==1&&y<=n+1) y++;
                 if (x>0&&y<=n&&s[x]!=s[y]){
                 a[1]=abs(b[x]-b[y]);
                 f1[1]=x,f2[1]=y;
                 work(1,top);
                 }
                 }
              else a[1]=a[top],f1[1]=f1[top],f2[1]=f2[top],top--,work(1,top);
              if (top==0) break;
              }
        if (top==0) break;
              }
    k=0;
    for (j=1;j<=i;j++)
        if (ans[j][0]!=0)
           k++;
    printf("%d\n",k);
    for (i=1;i<=k;i++)
        printf("%d %d\n",ans[i][0],ans[i][1]);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值