汉诺塔非递归实现
//汉诺塔递归版
#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();用的好