数据结构-栈

汉诺塔非递归实现

//汉诺塔递归版
#include<stdio.h>
//解决n个盘从a移到c,其中b为辅助的递归函数
void Hanoi(int n,int a,int b,int c){
    if(1==n){   //可以直接移动
        printf("%c -> %c\n",a,c);
    }else{  
        Hanoi(n-1,a,c,b);   //第一步 1~n-1号盘,从a柱移到b柱上
        Hanoi( 1 ,a,b,c);   //第二步 直接将n号盘,从a柱移到c柱上
        Hanoi(n-1,b,a,c);   //第三步 1~n-1号盘,从b柱移到c柱的n号盘上
    }
}   //其实也可以第二步替换为   printf("%c -> %c\n",a,c);   只是为了描述原理,统一更方便
int main() {
    int N;
    scanf("%d",&N);
    Hanoi(N,'a','b','c');
	return 0;
}
// 更新日期2022年3月26日
//7-17 汉诺塔的非递归实现 (25分)
#include<stdio.h>
#include<stdlib.h>
#define MAX_N 20    //测试点3 的N最大,达到20
#define MAX_SIZE MAX_N 
typedef struct{
    int n,a,b,c;//除了这些函数中的局部变量,系统递归还保存了很多寄存器的值,因为系统不知道那些数据才是关键的,所以只能全部打包保存。
    int step;   //完成第几步(与系统递归的差别在于,系统保存的是断点地址(下一个要执行的指令地址),由于只可能在递归子函数之后产生,故在模拟中只需设0~3四种状态来模拟断点)
}INFO;
INFO S[MAX_SIZE];   //栈
int top=-1; //栈顶指针
 
void push(int n,int a,int b,int c,int step){
    ++top;
#define PUSH_S(x) S[top].x=(x)
    PUSH_S(n);
    PUSH_S(a);
    PUSH_S(b);
    PUSH_S(c);
    PUSH_S(step);
    // 打印栈中元素信息
    if(top<0){puts("S[(n,step)]=[]\n");}
    else{
        printf("Stack=[ (%2d,%d)",S[0].n,S[0].step);
        for(int i=1;i<=top;i++){
            printf(",(%2d,%d)",S[i].n,S[i].step);
        }
        puts(" ]\n");
    }
 
}
void Hanoi_Simulation(){
#define SET(x) x=(S[top].x)
    assert(top>=0);
    {
        int SET(n),SET(a),SET(b),SET(c),SET(step);
        --top;
        switch(step){//系统的递归函数是用断点地址直接跳到要执行的那一步,这里用switch模拟。
            case 0: // step=0
                if(1==n){
                    printf("%c -> %c\n",a,c);
                }else {
                    push(n,a,b,c,step+1);           //保存本函数断点信息
                    push(n - 1, a, c, b, 0);    //调用子函数执行第一步
                }
                break;
            case 1: // step=1
                push(n,a,b,c,step+1);   //保存本函数断点信息
                push(1, a,b,c,0);   //调用子函数执行第二步
                break;
            case 2: // step=2
                push(n,a,b,c,step+1);   //保存本函数断点信息
                push(n-1,b,a,c,0);  //调用子函数执行第三步
            case 3: // step=3
                break;  //  看似没啥用,但实际上系统的递归是有这一步的,用于回收本函数占用的资源
        }
    }
}
 
//展示对应的递归版本
void Hanoi(int n,int a,int b,int c){
    //对应step=0
    if(1==n){   //可以直接移动
        printf("%c -> %c\n",a,c);
    }else{  
        Hanoi(n-1,a,c,b);   //第一步 1~n-1号盘,从a柱移到b柱上
        //对应step=1
        Hanoi( 1 ,a,b,c);   //第二步 直接将n号盘,从a柱移到c柱上
        //对应step=2
        Hanoi(n-1,b,a,c);   //第三步 1~n-1号盘,从b柱移到c柱的n号盘上
        //对应step=3
    }
}
int main() {
    int N;
    scanf("%d",&N);
    if(N>MAX_N)return 1;    //越界返回非零
    push(N,'a','b','c',0);  //0表示表示还需要判断n==1,而1则是准备执行到第1步了
    while(top>=0){  //栈非空则循环
        Hanoi_Simulation();
    }
	return 0;
}
//7-17 汉诺塔的非递归实现 (25分)
#include<stdio.h>
#define MAX_N 20    //测试点3 的N最大,达到20
#define MAX_SIZE (2*(MAX_N)-1)  //恰好能够解决问题的栈容量。如N=4时, 栈S最满时的INFO.n分别为[3,1,2,1,1,1,1 ] ;以此类推 N=n时为:[n-1,1,n-2,1,n-3,1,...,3,1,2,1,1,1,1]
typedef char ELE;
typedef struct{
    ELE n,id;
}INFO;
const char T[6][8] = {    //6种id对应的操作
	"a -> c",  // abc    000
	"a -> b",  // acb    001
	"b -> a",  // bca    010
	"b -> c",  // bac    011
	"c -> b",  // cab    100
	"c -> a"   // cba    101
};//正则:查找目标" (([abc])[abc]([abc]) )",替换为'\"$2 -> $3\\n\",  // $1'
/*
分析:每一步都是讲盘子从 a,b,c中的一根柱移到另一根上,所以有3*2=6种(排列数3选2),所以必然可以用6个编码代表6种移动方案。
关键是如何排列才能很方便地计算分解步骤的状态编码!
以初始状态为"a -> c"为例,第一步需要的是"a -> b",第二步不变还是"a -> c",第三步是"b -> c"。
先考虑第一步,起点不变只改变终点,可以将相同起点编码为相邻id,只需改变最低位(异或0x01)即可映射到第一步所需id;
第二步不变直接照抄;
至于第三步,终点不变换起点,同样是两种状态循环,那一共6种状态,不妨试试循环后移3格,abc变为bac则把bac设为id=3,类似格雷码,相邻两个id对应的排列都只交换两个字母(格雷码是改变其中1bit的0或1),很巧妙的发现,其对应id不过是循环后移(前移)3格。
*/
INFO S[MAX_SIZE];   //栈
int top=-1; //栈顶指针
void push(ELE n,ELE id){
    S[++top].n=n;
    S[top].id=id;
}
int main() {
    int N;
    scanf("%d",&N);
    push(N,0);  //abc
    while(top>=0){
        int n=S[top].n;
        int id=S[top].id;
        --top; //pop
        if(1==n){   //可以直接移动
            puts(T[id]);
        }else{
            push(n-1, (id+3)%6);//第三步 1~n-1号盘,从b柱移到c柱的n号盘上
            push(1 , id);       //第二步 直接将n号盘,从a柱移到c柱上
            push(n-1, (1^id));  //第一步 1~n-1号盘,从a柱移到b柱上
        }
    }
	return 0;
}

参考

出栈序列的合法性

给定一个最大容量为 M 的堆栈,将 N 个数字按 1, 2, 3, …, N 的顺序入栈,允许按任何顺序出栈,则哪些数字序列是不可能得到的?例如给定 M=5、N=7,则我们有可能得到{ 1, 2, 3, 4, 5, 6, 7 },但不可能得到{ 3, 2, 1, 7, 5, 6, 4 }。

输入格式:
输入第一行给出 3 个不超过 1000 的正整数:M(堆栈最大容量)、N(入栈元素个数)、K(待检查的出栈序列个数)。最后 K 行,每行给出 N 个数字的出栈序列。所有同行数字以空格间隔。

输出格式:
对每一行出栈序列,如果其的确是有可能得到的合法序列,就在一行中输出YES,否则输出NO。

输入样例:
5 7 5
1 2 3 4 5 6 7
3 2 1 7 5 6 4
7 6 5 4 3 2 1
5 6 4 3 7 2 1
1 7 6 5 4 3 2
输出样例:
YES
NO
NO
YES
NO

#include <bits/stdc++.h>
#define fast_io ios::sync_with_stdio(false); cin.tie(NULL); cout.tie(NULL)
#define all(x) (x).begin(), (x).end()
#define rall(x) (x).rbegin(), (x).rend()
using namespace std;
typedef long long ll;
typedef long double ld;



int main() {
    fast_io;
    clock_t start, end;
    start = clock();
    int m, n, k;
    int stack[1010], b[1010];
    cin >> m >> n >> k;
    while (k--) {
        int index1 = 1, index2 = 1;
        int top = 0;
        bool flag = true;
        for (int i = 1; i <= n; i++) {
            cin >> b[i];
        }

        while (1) {
            

            if (index1 == b[index2]) {
                index1++;
                index2++;
            }
            else if (top != 0 && stack[top - 1] == b[index2]) {
                top--;
                index2++;
            }
            else {
                if (index1 > n)break;
                stack[top++] = index1++;

                if (top >= m) {
                    flag = false;
                    break;
                }
            }
        }
        if (top != 0 || flag == false) {
            cout << "NO" << endl;
        }
        else {
            cout << "YES" << endl;
        }
    }
    
    end = clock();
    //cout << "time=" << double(end - start) / CLOCKS_PER_SEC << "s"<<endl;

}

7-4 盲盒包装流水线

分数 300
作者 陈越
单位 浙江大学
众所周知,PAT 有 9 枚徽章,分别对应青铜、白银、黄金、白金、钻石、大师、王者、大圣、天神这 9 个段位,只有成绩非常优秀的考生才有资格获得刻有自己名字的徽章。现在,PAT 制作了徽章的小型纪念版,要制成盲盒给大家玩了!

下图是一条盲盒包装流水线的示意图。首先徽章通过进货口被压入货栈里,空盒在履带上从左向右传送。每次从货栈里弹出一枚徽章,进入打包机,装入一只空盒,打包后继续向右边传送。当货栈为空时,打包机会暂停,等待下一批徽章压入货栈。

lsx.png

每只盒子都有一个编号,小拼姐姐手里有进入流水线的空盒编号顺序表,也有每一批送往货栈的徽章顺序表,这样她其实可以知道每只盒子里装了哪种徽章。有些小朋友收到了盲盒,就想在拆封前问无所不知的小拼姐姐,盒子里的徽章是哪一种。但是因为盲盒总量有 10
5
这么多,小拼姐姐可记不住每只盒子里装的是什么,于是你就被请来写个程序帮小拼姐姐回复这种信息。

输入格式:
输入第一行给出 2 个正整数,分别为盲盒总量 N(≤10
5
)和货栈容量 S(≤100)。接下来一行给出 N 只盒子的编号,编号由 5 位数字组成,给出的顺序是空盒进入传送带的顺序。随后 N/S(保证是整数)行,每行给出一批 S 枚徽章的类型,为 1-9 的数字,给出的顺序是从进货口入栈的顺序。

再下面给出一个正整数 K(≤10
4
),为查询次数。随后 K 行,每行给出一个 5 位编号。

输出格式:
对每个查询编号,在一行中输出该盒子中装的徽章类型。如果编号是错误的,则在一行中输出 Wrong Number。

输入样例:
10 5
00132 10093 92001 23333 66666 88888 09009 34658 82750 69251
1 2 3 4 5
9 8 7 6 1
5
66666
88888
69251
55555
10093

输出样例:
1
1
9
Wrong Number
4

代码长度限制
16 KB
时间限制
150 ms
内存限制
64 MB

#include<bits/stdc++.h>
#define llu unsigned long long
using namespace std;
int a[100010];
queue<int> q;
stack<int> c[50010];
int main(){
	int n,s;
	cin >> n >> s;
	for(int i=0;i<n;i++)
	{
		int x;
		cin >> x ;
		q.push(x);
	}
	for(int i=0;i<n/s;i++){
		for(int j=0;j<s;j++){
			int x;
			cin >> x;
			c[i].push(x);
		}
	}
	for(int i=0;i<n;){
		for(int j=0;j<s;j++,i++){
			int x=q.front();
			q.pop();
			int y=c[i/s].top();
			c[i/s].pop();
			a[x]=y;
		}
	}
	int k;
	cin >> k;
	for(int i=0;i<k;i++){
		int x;
		cin >> x ;
		if(a[x]==0)cout << "Wrong Number" << endl ;
		else cout << a[x] << endl ;
	}
	return 0;
}


分析:c[i/s].pop();用的好

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值