舞蹈课(dancinglessons)

xiaoxiao2021-02-28  79

舞蹈课(dancinglessons)

洛谷p1878

题目大意

n个人排成一列,给出n个人的性别及a[i]值,每次让a[i]值相差最小的两个异性出列,输出这样的对数及两人的编号。n<=200000。

题解

50分算法

对于第一个问题,只需要输出男生和女生中个数少的那一个,因为最终一定有一方全部出列,不然肯定有男生和女生相邻,就会继续出列。 对于第二个问题,构建一个结构体node,来储存每个人的所有信息。这和约瑟夫问题有异曲同工之妙,我们可以把每个人的位置(编号)作为元素,建立双向链表,于是可以把编号作为node里的下标,知道这个元素的所有信息。链表的好处是可以O(1)时间删除链表中元素,双向的好处是可以方便地找到每个元素的前驱和后继。美中不足的是,每次让最小的一对出列需要扫描整个链表,所以复杂度是O(n^2)。 代码如下。

#include<iostream> #include<cstdio> #include<cmath> using namespace std; #define INF 1000000000 struct node { char c; int data,pre,next; }a[200005]; int n; int main() { cin>>n; int g=0,b=0; for (int i=1;i<=n;i++) { cin>>a[i].c; if (a[i].c=='G') g++; else b++; } for (int i=1;i<=n;i++) { cin>>a[i].data; a[i].pre=i-1; a[i].next=i+1; } a[0].next=1; cout<<min(g,b)<<'\n'; for (int i=1;i<=min(g,b);i++) { int x=a[0].next,y=INF,z; while (a[x].next!=n+1) { int t=a[x].next; if (a[x].c==a[t].c) { x=a[x].next; continue; } if (abs(a[x].data-a[t].data)<y) { y=abs(a[x].data-a[t].data); z=x; } x=a[x].next; } cout<<z<<' '<<a[z].next<<'\n'; a[a[z].pre].next=a[a[z].next].next; a[a[a[z].next].next].pre=a[z].pre; } }
100分算法

双向链表可以保留。考虑如何快速找到最小的那一对。于是想到可以把每一对作为一个元素,放进优先队列里。 如果不会优先队列,可以点这里。 我是用node1把每一对打包,放入优先队列里。每次弹出队头,如果这一对中有人已出列,就跳过,不然,他们就出列。出列之后,要看一看两边新组成的这一对是不是异性,如果是,就入队。当然,双向链表还是需要的,不然找不到元素的前驱和后继。 因为优先队列内部是用堆实现的,所以每次操作的复杂度为O(logn),总复杂度为O(nlogn)。 具体实现如下。

#include<iostream> #include<cstdio> #include<queue> #include<cmath> using namespace std; int n,b[200005]; struct node { char c; int data,pre,next; } a[200005]; struct node1 { int x1,x2; node1() {} node1(int x,int y):x1(x),x2(y) {} bool operator <(node1 x) const { if (abs(a[x1].data-a[x2].data)!=abs(a[x.x1].data-a[x.x2].data)) return abs(a[x1].data-a[x2].data)>abs(a[x.x1].data-a[x.x2].data); return x1>x.x1; } }; priority_queue<node1> p; int main() { cin>>n; int g=0; for (int i=1; i<=n; i++) { cin>>a[i].c; if (a[i].c=='G') g++; } for (int i=1; i<=n; i++) { cin>>a[i].data; a[i].pre=i-1; a[i].next=i+1; } cout<<min(g,n-g)<<'\n'; for (int i=1; i<n; i++) { if (a[i].c!=a[i+1].c) { p.push(node1(i,i+1)); } } while (!p.empty()) { node1 x; x=p.top(); p.pop(); if (x.x1==0||x.x2==n+1) continue; if (b[x.x1]==1||b[x.x2]==1) continue; b[x.x1]=b[x.x2]=1; cout<<x.x1<<' '<<x.x2<<'\n'; if (a[a[x.x1].pre].c!=a[a[x.x2].next].c) p.push(node1(a[x.x1].pre,a[x.x2].next)); a[a[x.x1].pre].next=a[x.x2].next; a[a[x.x2].next].pre=a[x.x1].pre; } }

补充

关于结构体,还想再说一点。第2个程序第14行的是构造函数,可以把2个数转换为node1类型(请看第36行)。第13行本来可以不用写的,不过有了构造函数就一定要有这个函数,不然将无法定义node1类型的数(删去第13行后,第40行就写不了了)。

总结

在结构体里重载<的时候,要注意优先队列是个大根堆,若想要小的在前面,return后面就应该写>,跟它反着来。 这是我写的第一篇博客,要是有什么不足之处,望大家指出。

转载请注明原文地址: https://www.6miu.com/read-2632599.html

最新回复(0)