您的位置:首页 > 其它

hdu 5818 Joint Stacks 2016 Multi-University 7

2016-08-10 17:53 351 查看
Problemacm.hdu.edu.cn/showproblem.php?pid=5818

官方题解bestcoder.hdu.edu.cn/blog/2016-multi-university-training-contest-7-solutions-by-sysu/

题意:两个栈,支持push、pop、merge 3种操作

push x y :把 y 压入栈 x

pop x :栈 x 弹出一个元素,输出它的值

merge x y :把 y 中的元素全部移到 x 中,并且保持所有元素的进栈时的先后顺序

分析:官方题解中有种解法,是开第3个栈,当要合并的时候把两个栈里的元素全部放到这个栈里,并且清空原来两个栈

我理解:

#include <stdio.h>
#define N 100000
struct
{
int v;
int id; // 标明元素的进栈先后
} a
, b
, c
; // 3个栈
int main()
{
int n,kase=1;
while( ~scanf("%d%*c",&n) && n )
{
printf("Case #%d:\n",kase++);
// 3个栈的栈顶指针
int topa = 0, topb = 0, topc = 0;
// “时间”标明元素进栈先后
int time;
// own标记c中的元素属于A还是B
char own = '\0';
for(time=0; time<n; time++)
{
char op[7],who;
int val,i,j;
scanf("%s %c%*c",op, &who);
if( op[1] == 'u' ) // push
{
scanf("%d%*c", &val);
if( who == 'A' )
{
a[topa].v = val;
a[topa++].id = time;
}
else
{
b[topb].v = val;
b[topb++].id = time;
}
}
else if( op[1] == 'o' ) // pop
{
if( who == 'A' )
{
if( topa )
4000
// 如果a栈中有元素,必是最新的
printf("%d\n", a[--topa].v);
else if( topc && own == 'A' ) // c中有元素且属于A
printf("%d\n", c[--topc].v);
}
else
{
if( topb )
printf("%d\n", b[--topb].v);
else if( topc && own == 'B' )
printf("%d\n", c[--topc].v);
}
}
else // merge
{
// 读掉第2个参数
scanf("%*s");
// 把a、b栈的元素都放进c栈
for(i=j=0; i<topa || j<topb; )
if( i<topa && j<topb ) //a、b栈都非空
{
if( a[i].id < b[j].id ) // 比较入栈顺序
{
c[topc].v = a[i].v;
c[topc++].id = a[i++].id;
}
else
{
c[topc].v = b[j].v;
c[topc++].id = b[j++].id;
}
}
else if( i<topa ) // a栈非空
{
c[topc].v = a[i].v;
c[topc++].id = a[i++].id;
}
else // b栈非空
{
c[topc].v = b[j].v;
c[topc++].id = b[j++].id;
}
// 清空a、b栈
topa = topb = 0;
// 第1个参数表示归属
own = who;
}
}
}
return 0;
}
可以开两个栈,记录两个栈的栈顶指针(topa,topb)和各自的一个标记指针(marka,markb)

要合并时,比如 merge A B,就把 b 栈的标记指针打到 a 栈的栈顶指针那,表示此标记开始以下的元素都属于 b 栈

同时把 a 栈在 b 栈上的标记指针打到 b 栈的栈底

那么属于 a 栈的元素就包括:a 栈中从栈顶指针 topa 到 b 的标记指针 markb 中间一段的元素,和 b 栈中从 a 栈的标记指针 marka 到 b 栈栈底一段的元素

要弹栈时,比如 pop B,就要比较 topb 所指元素(如果有)与 markb 所指元素(如果有)的进栈顺序,输出较新的一个,并且删掉那个元素

用双向链表实现栈(因为删元素的时候要把与它相邻的两个元素连起来)

注意:如果 pop B 的时候要删的是 markb 所指的元素,而 topa == markb,那么在删元素前更改 markb 的指向时,要把 topa 也一同改了(不然 topa 成野指针)

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

typedef struct Node
{
int v,id;
struct Node *pre,*next; // 上面和下面
}node;

void push(node **s, int val, int tm)
{
node *p = malloc( sizeof( node ) );
p->v = val;
p->id = tm;
p->pre = NULL;
p->next = *s;
if(*s)
(*s)->pre = p;
*s = p;
}
/* 删除元素 */
void erase(node **me, node **histop)
{
node *q = *me;
printf("%d\n", q->v);
if( q->pre )
q->pre->next = q->next;
if( q->next )
q->next->pre = q->pre;
*me = q->next;
if( *histop == q )
*histop = *me;
free(q);
}

void pop(node **mytop, node **mymark, node **histop, node *hismark)
{
// 我栈中有属于我的元素,他栈中没有
if( *mytop != hismark && !*mymark )
erase(mytop,histop);
// 我栈没有,他栈有
else if( *mytop == hismark && *mymark )
erase(mymark,histop);
// 我有他有
else if( *mytop != hismark && *mymark )
if( (*mytop)->id < (*mymark)->id )
erase(mymark,histop);
else
erase(mytop,histop);
}

void merge(node **mymark, node **hismark, node *histop)
{
// 标记打到对方栈顶
*mymark = histop;
// 对方的标记打到“栈底”
*hismark = NULL;
}
/* 销毁链表 */
void destroy(node *s)
{
while(s)
{
node *q = s;
s = s->next;
free(q);
}
}

int main()
{
int n,kase=1;
while( ~scanf("%d%*c",&n) && n )
{
int i;
// 栈顶指针,标记指针
node *topa,*topb,*marka,*markb;
printf("Case #%d:\n", kase++);
topa = topb = marka = markb = NULL;
for(i=0; i<n; i++)
{
char op[7],who;
int val;
scanf("%s %c%*c", op, &who);
if( op[1] == 'u' )
{
scanf("%d%*c", &val);
if( who == 'A' )
push(&topa, val, i);
else
push(&topb, val, i);
}
else if( op[1] == 'o' )
{
if( who == 'A' )
pop(&topa, &marka, &topb, markb);
else
pop(&topb, &markb, &topa, marka);
}
else
{
scanf("%*s");
if( who == 'A' )
merge(&marka, &markb, topb);
else
merge(&markb, &marka, topa);
}
}
// 销毁链表
destroy(topa);
destroy(topb);
}
return 0;
}
本来想用C++的 vertor 来写栈,删元素时直接用它的 erase(),然而wa了…不知道它删完一个元素后,是不是后面元素的下标都自动 -1 还是怎么变,还是因为写漏了个冒号…
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐