您的位置:首页 > 理论基础 > 数据结构算法

数据结构_优先队列

2014-11-29 19:13 323 查看
数据结构与算法实验题 11.4 4 舞蹈课

★ 实验任务

有 n 个人参加舞蹈课,每个人以他的舞蹈技能 ai 为特点。舞蹈课开始

前,他们从左到右排成一行。当队伍中至少含有一名男生和一名女生时,下

面的过程会重复进行:

站在相邻位置的男女生且舞蹈技能差异最小的开始跳舞。 如果有很多对,

最靠左边的开始跳舞。跳舞完以后就离开。

舞蹈技能的差异为|ai-aj|。你的任务是找到每一对,并找出是按什么顺

序开始跳舞。

★ 数据输入

输入的第 1 行是一个正整数 n(1 <= n <= 2 * 10^5),表示人的个数。

第 2 行包含 n 个字符”B”或”G”,分别代表男生和女生,之间没有用空格

隔开。第 3 行包含 n 个整数 ai (1 ≤ ai ≤ 10^7),表示每个人的舞蹈技能。按

从左到右的顺序排列。

★ 数据输 出

输出 k 对。接下去输出 k 行,每行包含 2 个整数,表示组成一对的男女

生的序号。 按从左到右从 1 到 n 开始排序。 当一对舞者离开时不用重新排序。

打印每一对时按递增打印。按跳舞的顺序打印。

输入示例 输出示例

4

BGBG

4 2 4 3

2

3 4

1 2

14

BGBGBBGGGBBBBG

4 4 4 4 5 6 7 8 9 10 11 12 5 5

6

1 2

3 4

13 14

6 7

9 10

5 8

14

BGBGBBGGGBBBBG

4 4 4 4 5 6 7 8 9 9 8 7 6 5

6

1 2

3 4

9 10

8 11

7 12

13 14

5

BGGBB

25 4 3 2 1

2

3 4

2 5

8

BGBGBGBG

100 1 10 10 4 6 100 96

4

3 4

5 6

7 8

1 2

以下差值均默认为绝对值下的

1.每次选出男女相邻的差值(绝对值)最小的,并输出TA们下标

问题1).每个人与他的前面那个人和后面那个人各有一个差值,该怎么表示?

ans:作出如下规定:用数组sub来记录sub[i]表示i与i+1的差值

问题2).选取的必须是异性间的最小的差值,该怎么处理?

ans:可以把同性的差值+同一个足够大的数,

因为每个差值最大不会大于10^7,因此这个数可以是2*10^7。

问题3).每走掉一对男女,都会对其他人的差值造成影响,该怎么处理这种影响?

可能的影响有:1.sub改变了2.下标i-1,i+1可能已经走掉了

1.sub改变了:

比如

5

BGGBB

25 4 3 2 1

原来sub[2]=4-3+2*10^7,走掉3,4后,会变成sub[2]=4-1;

key--->发现只对前面的sub有影响!

2.下标i-1,i+1可能已经走掉了:

key->>>>第二对走的是2,5 怎么知道2的后面那个数是5?

用next_step[i]表示i的后一个未走的人的下标

每次走掉一对记得更新该数组

方法1:(较慢,测试得大概是方法2的1.5倍时间,且内存消耗也较大)

#include<cstdio>
#include<cstring>
#define F 20000000
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
const int N=200100;
bool data
,can_dance
;
char str
;
int pre_step
,sub
,next_step
;
struct node
{
int index,sub;
};
node ready_dance
;
node sum[N*3];
node min(const node &a,const node &b){
if(a.sub!=b.sub)
return a.sub<b.sub?a:b;
else
return a.index<b.index?a:b;
}
int cnt=1;
//!线段树单点更新
void push_up(int rt){
sum[rt]=min(sum[rt<<1],sum[rt<<1|1]);
}
void build(int l,int r,int rt)
{
if(l==r)
{
sum[rt]=ready_dance[cnt++];
return;
}
int m=(l+r)>>1;
build(lson);build(rson);
push_up(rt);
}
void update2(int cur_i,int next_i,int n)
{
int pre,cur;
if(cur_i==0)   return;//第一个点的前面没有点
pre=sub[cur_i],cur=sub[next_i];
ready_dance[cur_i].sub=cur-pre<0?pre-cur:cur-pre;
if(!(data[cur_i]^data[next_i])||next_i>n)ready_dance[cur_i].sub+=F;
}
void update(int x,int c,int l,int r,int rt)
{
if(l==r)
{
sum[rt].sub=c;
ready_dance[x].sub=F+F;//相当于删除改点
return ;
}
int m=(l+r)>>1;
if(x<=m) update(x,c,lson);
else update(x,c,rson);
push_up(rt);
}
//!线段树单点更新

void Init()
{
int i;
pre_step[0]=-1;
for(i=1;i<N;++i){
pre_step[i]=i-1;
next_step[i-1]=i;
}
}
int main()
{
Init();
int n,i,pre,cur,girls,boys;
scanf("%d",&n);
scanf("%s",str);
//!data数组记录男女相对位置
for(i=0,girls=boys=0;i<n;++i)
if(str[i]=='B'){
++boys;
data[i+1]=true;
} else ++girls;
//!data数组记录男女相对位置
//!sub数组记录第k数与第k+1数的差绝对值,read_dance数组记录差值与原下标
for(i=1;i<=n;++i){
scanf("%d",&sub[i]);
cur=sub[i];
if(i>1){
ready_dance[i-1].sub=cur-pre<0?pre-cur:cur-pre;
if(!(data[i-1]^data[i]))ready_dance[i-1].sub+=F;
ready_dance[i-1].index=i-1;
}
pre=cur;
}
ready_dance
.sub=F+F;//F+F这个值足够大,可以保证在选择时不被选中(相当于排除该点)
//!sub数组记录第k数与第k+1数的差绝对值,read_dance数组记录差值与原下标

//!这里pre_step[i]与next_step[i],分别表示i原下标的前一个原下标和后一个原下标
//!不能根据i+1,i-1来表示下标i的后面与前面,i+1,i-1可能在之前已经走掉~
build(1,n,1);
node tmp; node *print=&tmp;
printf("%d\n",girls<boys?girls:boys);
while((girls>0&&boys>0)){
tmp=sum[1];
pre_step[next_step[next_step[print->index]]]=pre_step[print->index];
next_step[pre_step[print->index]]=next_step[next_step[print->index]];
update2(pre_step[print->index],next_step[pre_step[print->index]],n);
update(pre_step[print->index],ready_dance[pre_step[print->index]].sub,1,n,1);//更新前面点
update(print->index,3*F,1,n,1);update(next_step[print->index],3*F,1,n,1);//删除现在头结点
printf("%d %d\n",print->index,next_step[print->index]);
--girls,--boys;
}
}


自己写优先队列(速度较快)

#include<stdio.h>
#include<malloc.h>
#define parent cur>>1
#define cur_lson cur<<1
#define cur_rson cur<<1|1
const int N=200100;
struct node
{
int index,sub;
};
typedef node SetItem;
typedef struct minheap *Heap;
typedef struct minheap {
int last;
SetItem *heap;
}Minheap;
bool data
,vis
;
char str
;
int pre_step
,sub
,next_step
;
node ready_dance
;
Minheap  q;
Heap H;
bool less(const  SetItem&a,const SetItem &b)
{
if(a.sub!=b.sub)
return a.sub<b.sub;
else
return a.index<b.index;
}
void Init()
{
int i;
pre_step[0]=-1;
for(i=1;i<N;++i){
pre_step[i]=i-1;
next_step[i-1]=i;
}
}
void MinHeapInit()
{
H=(Heap)malloc(sizeof(*H));
H->heap=(SetItem*)malloc((N+1)*sizeof(SetItem));
H->last=0;
}
void max_heapify(int cur,int cur_son,SetItem last)
{
while(cur_son<=H->last){
if(cur_son<H->last&&less(H->heap[cur_rson],H->heap[cur_lson]))cur_son=cur_rson;
if(!less(H->heap[cur_son],last))break;
H->heap[cur]=H->heap[cur_son];
cur=cur_son;
cur_son=cur_lson;
}
H->heap[cur]=last;
}
void push(SetItem x)
{
int cur;
//    if(H->last==H->maxsize) return ;
cur=++H->last;
while(cur!=1&&less(x,H->heap[parent])){
H->heap[cur]=H->heap[parent];
cur=parent;
}
H->heap[cur]=x;
}
SetItem top_pop()
{
int cur,cur_son;
SetItem top,last;
top=H->heap[1];
last=H->heap[H->last--];
cur=1,cur_son=cur_lson;
max_heapify(cur,cur_son,last);
return top;
}
int main()
{
Init();MinHeapInit();
int n,i,pre,cur,girls,boys;
scanf("%d",&n);
scanf("%s",str);
for(i=0,girls=boys=0;i<n;++i)
if(str[i]=='B'){
++boys;
data[i+1]=true;
} else ++girls;

for(i=1;i<=n;++i){
scanf("%d",&sub[i]);
cur=sub[i];
if(i>1){
ready_dance[i-1].sub=cur-pre<0?pre-cur:cur-pre;
ready_dance[i-1].index=i-1;
if((data[i-1]^data[i]))push(ready_dance[i-1]);
}
pre=cur;
}
node tmp;node *print=&tmp;
printf("%d\n",girls<boys?girls:boys);
int nextstep,prestep;
while((girls>0&&boys>0)){
tmp=top_pop();

if(vis[print->index]||!(data[print->index]^data[next_step[print->index]]))continue;
if(print->sub!=ready_dance[print->index].sub)continue;
pre_step[next_step[next_step[print->index]]]=pre_step[print->index];
next_step[pre_step[print->index]]=next_step[next_step[print->index]];

nextstep=next_step[pre_step[print->index]],
prestep=pre_step[print->index];
ready_dance[prestep].sub=sub[prestep]-sub[nextstep]<0?(sub[nextstep]-sub[prestep]):(sub[prestep]-sub[nextstep]);
if(prestep>0&&nextstep<=n&&(data[prestep]^data[nextstep]))
{

push(ready_dance[prestep]);
}
printf("%d %d\n",print->index,next_step[print->index]);
--girls,--boys;
vis[print->index]=vis[next_step[print->index]]=true;
}
}


方法2:

1.最初把所有满足与后面相邻点为异性的点push到优先队列中

2.每次取出q.top();并且取出点需要满足2个条件才可以输出 print->index,next_step[print->index];

1)条件一:该点未被访问过;

2)条件二:该结点的sub必须等于最后更新的sub值;这个十分重要,因为每个点的sub值可能被更新数次!其中可能数次满足被push的条件。

3.在每次取出q.top()后,q.pop();并且更新pre_step,next_step数组,同时更新ready_dance[pre_step[print->index]](即出去点的前相邻点的sub值),如果出去点的前相邻点满足与更新后的后相邻点位异性,那么push它

ps:

条件二的具体分析:

8

BGBGBGBG

100 1 10 10 4 6 100 96

1.这八个点都满足push条件,因此全部push

循环1:

2.队列top()为结点3

检查是否满足2个条件,发现是的

输出3 4

3.更新

pre_step[next_step[next_step[3]]]=pre_step[3];

next_step[pre_step[3]]=next_step[next_step[3]]];

更新 ready_dance[2].sub=4-1=3并且被push

同时发现更新后:

read_dance[2].sub=4-1=3;且第2点与后相邻的第4点为异性,所以push

循环2:

队列top()为结点5

......结点2被第二次更新

循环3:

队列top()为结点2,当初结点2第一次后sub=3,虽然后来又被更新为99但第一次更新的已经被Push

此时发现 print->sub!=ready_dance[print->index].sub(也就是不满足条件2,因此不输出)

...

方法2:(较快)

/*
8
BGBGBGBG
100 1 10 10 4 6 100 96
*/

#include<cstdio>
#include<cstring>
#include<queue>
#define F 20000000
using namespace std;
const int N=200100;
bool data
;
char str
;
int pre_step
,sub
,next_step
;
bool vis
;
struct node
{
int index,sub;
bool operator <(const node &b)const{
if(sub!=b.sub)
return sub>b.sub;
else
return index>b.index;
}
};
node ready_dance
;
priority_queue<node> q;
int cnt=1;
void Init()
{
int i;
pre_step[0]=-1;
for(i=1;i<N;++i){
pre_step[i]=i-1;
next_step[i-1]=i;
}
}
int main()
{
Init();
int n,i,pre,cur,girls,boys;
scanf("%d",&n);
scanf("%s",str);
//!data数组记录男女相对位置
for(i=0,girls=boys=0;i<n;++i)
if(str[i]=='B'){
++boys;
data[i+1]=true;
} else ++girls;
//!data数组记录男女相对位置

//!sub数组记录第k数与第k+1数的差绝对值,read_dance数组记录差值与原下标
for(i=1;i<=n;++i){
scanf("%d",&sub[i]);
cur=sub[i];
if(i>1){
ready_dance[i-1].sub=cur-pre<0?pre-cur:cur-pre;
ready_dance[i-1].index=i-1;
if((data[i-1]^data[i]))q.push(ready_dance[i-1]);

}
pre=cur;
}
//!sub数组记录第k数与第k+1数的差绝对值,read_dance数组记录差值与原下标

//!这里pre_step[i]与next_step[i],分别表示i原下标的前一个原下标和后一个原下标
//!不能根据i+1,i-1来表示下标i的后面与前面,i+1,i-1可能在之前已经走掉~

node tmp;node *print=&tmp;
printf("%d\n",girls<boys?girls:boys);
int nextstep,prestep;
while((girls>0&&boys>0)){
tmp=q.top();q.pop();
// printf("cur_index=%d cur_next_index=%d\n",print->index,next_step[print->index]);
if(vis[print->index]||!(data[print->index]^data[next_step[print->index]]))continue;
if(print->sub!=ready_dance[print->index].sub)continue;//有些点会被更新数次,而且每次都会被push进去,必须确保pop出来的与最后一次更新的信息一致
pre_step[next_step[next_step[print->index]]]=pre_step[print->index];
next_step[pre_step[print->index]]=next_step[next_step[print->index]];

nextstep=next_step[pre_step[print->index]],
prestep=pre_step[print->index];
ready_dance[prestep].sub=sub[prestep]-sub[nextstep]<0?(sub[nextstep]-sub[prestep]):(sub[prestep]-sub[nextstep]);
if(prestep>0&&nextstep<=n&&(data[prestep]^data[nextstep]))
{
//  printf("prestep==%d nextstepp=%d\n",prestep,nextstep);
q.push(ready_dance[prestep]);
}
printf("%d %d\n",print->index,next_step[print->index]);
--girls,--boys;
vis[print->index]=vis[next_step[print->index]]=true;
// show(n);

}
}


自己写队列

#include<stdio.h>
#include<malloc.h>
#define parent cur>>1
#define cur_lson cur<<1
#define cur_rson cur<<1|1
const int N=200100;
struct node
{
int index,sub;
};
typedef node SetItem;
typedef struct minheap {
int last;
SetItem *heap;
}Minheap;
typedef struct minheap *Heap;
bool data
,vis
;
char str
;
int pre_step
,sub
,next_step
;
node ready_dance
;
Heap H;
bool less(const  SetItem&a,const SetItem &b)
{
if(a.sub!=b.sub)
return a.sub<b.sub;
else
return a.index<b.index;
}
void Init()
{
int i;
pre_step[0]=-1;
for(i=1;i<N;++i){
pre_step[i]=i-1;
next_step[i-1]=i;
}
}
void MinHeapInit()
{
H=(Heap)malloc(sizeof(*H));
H->heap=(SetItem*)malloc((N+1)*sizeof(SetItem));
H->last=0;
}
void max_heapify(int cur,int cur_son,SetItem last)
{
while(cur_son<=H->last){
if(cur_son<H->last&&less(H->heap[cur_rson],H->heap[cur_lson]))cur_son=cur_rson;
if(!less(H->heap[cur_son],last))break;
H->heap[cur]=H->heap[cur_son];
cur=cur_son;
cur_son=cur_lson;
}
H->heap[cur]=last;
}
void push(SetItem x)
{
int cur;
//    if(H->last==H->maxsize) return ;
cur=++H->last;
while(cur!=1&&less(x,H->heap[parent])){
H->heap[cur]=H->heap[parent];
cur=parent;
}
H->heap[cur]=x;
}
SetItem top_pop()
{
int cur,cur_son;
SetItem top,last;
top=H->heap[1];
last=H->heap[H->last--];
cur=1,cur_son=cur_lson;
max_heapify(cur,cur_son,last);
return top;
}
int main()
{
Init();MinHeapInit();
int n,i,pre,cur,girls,boys;
scanf("%d",&n);
scanf("%s",str);
//!data数组记录男女相对位置
for(i=0,girls=boys=0;i<n;++i)
if(str[i]=='B'){
++boys;
data[i+1]=true;
} else ++girls;
//!data数组记录男女相对位置

//!sub数组记录第k数与第k+1数的差绝对值,read_dance数组记录差值与原下标
for(i=1;i<=n;++i){
scanf("%d",&sub[i]);
cur=sub[i];
if(i>1){
ready_dance[i-1].sub=cur-pre<0?pre-cur:cur-pre;
ready_dance[i-1].index=i-1;
if((data[i-1]^data[i]))push(ready_dance[i-1]);

}
pre=cur;
}
//!sub数组记录第k数与第k+1数的差绝对值,read_dance数组记录差值与原下标

//!这里pre_step[i]与next_step[i],分别表示i原下标的前一个原下标和后一个原下标
//!不能根据i+1,i-1来表示下标i的后面与前面,i+1,i-1可能在之前已经走掉~

node tmp;node *print=&tmp;
printf("%d\n",girls<boys?girls:boys);
int nextstep,prestep;
while((girls>0&&boys>0)){
tmp=top_pop();
// printf("cur_index=%d cur_next_index=%d\n",print->index,next_step[print->index]);
if(vis[print->index]||!(data[print->index]^data[next_step[print->index]]))continue;
if(print->sub!=ready_dance[print->index].sub)continue;//有些点会被更新数次,而且每次都会被push进去,必须确保pop出来的与最后一次更新的信息一致
pre_step[next_step[next_step[print->index]]]=pre_step[print->index];
next_step[pre_step[print->index]]=next_step[next_step[print->index]];

nextstep=next_step[pre_step[print->index]],
prestep=pre_step[print->index];
ready_dance[prestep].sub=sub[prestep]-sub[nextstep]<0?(sub[nextstep]-sub[prestep]):(sub[prestep]-sub[nextstep]);
if(prestep>0&&nextstep<=n&&(data[prestep]^data[nextstep]))
{
//  printf("prestep==%d nextstepp=%d\n",prestep,nextstep);
push(ready_dance[prestep]);
}
printf("%d %d\n",print->index,next_step[print->index]);
--girls,--boys;
vis[print->index]=vis[next_step[print->index]]=true;
// show(n);

}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: