UVa514铁轨
这道题有两个做法。
- 思路一:维护状态扫序列
给每个序号维护一个状态,0表示未进入中转站,1表示在中转站中,2表示已出站。遍历待判断序列。
对于当前序号,如果之前已经要求它出栈,即状态已经等于2,而现在才出栈,肯定不对;如果现在的状态等于1,在栈里,可能可以出栈,这就要求比它大的要么出栈了要么没来;如果现在的状态等于0,还未进入中转站,就需要它能够进栈,也就是比它小的要么出栈了要么在栈里。
因此,对编号大于它的,状态只能是0或者2,否则不可能。对编号小于它的,只要不是出栈状态就一定在栈里,把状态给它们刷过去。
出现矛盾的就不可能。
- 思路二:模拟栈直接走
直接比着目标序列,看能不能走得出来。
用两个指针模拟A和B,A就是1-N的顺序序列指针,表示待进入中转站的所有列车,B就是目标序列的指针,指向当前要造的那个编号。
注意以哪个指针作为终止条件。如果以A做条件,不知道当前中转站出栈出到什么时候A才入栈,A指针根本不知道何时才能往后走,而B指针很明确,能匹配就走,不能匹配就一直停在那儿。
以A做条件明显复杂很多,而且很容易出错,复杂如下:
如果当前站台(A)上的编号就是目标序列的编号,那么当前编号的列车直接入中转站再直接出栈,A和B直接都+1。如果当前中转站里面的栈顶是,就出栈一直到不能出为止,B+1而A不动。如果当前中转站里面的栈顶不是,再看站台上的,如果当前站台上的不是目标序列的编号,那么就入栈,兴许后续才出栈,A+1而B不动。当B移动之后,中转站的栈顶又可能满足了,所以又要判断一下,这样其实就可以无穷无尽下去了。
以B做条件会简单清晰很多,以B来移动,如果当前车站上的能满足,就A和B都+1,并让B往后循环;如果当前中转站的能满足,就B+1而A不动,并让B往后循环;如果车站上的和中转站的都不能满足,车站上的进栈,A+1而B不动;如果车站上的和中转站的都不满足而且车站上的已经没有了,break出去,能用的A都用完了。
如果A走完了一遍,目标序列还没有造出来,就不可能。
思路二会写栈,思路一不用写栈。思路二在练习数据结构上更本质而且更快。
- 实现数据结构的tips:
下溢拿来用top = -1。
实现数据结构函数的时候就把判空判满的情况考虑到,比如取栈顶,就在这个函数里面判空,如果是空就返回一个目标不可能取到的值。
思路一代码:
// UVa514铁轨
// 思路一:扫序列
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int MAXN = 1010;
int order[MAXN];
int state[MAXN]; // 0表示未进入中转站,1表示在栈中,2表示已经出栈
int N;
int main() {
freopen("data.in", "r", stdin);
while (scanf("%d", &N) == 1 && (N != 0)) {
int temp = -1;
while (scanf("%d", &temp) == 1 && (temp != 0)) {
memset(state, 0, sizeof(state));
order[1] = temp;
for (int i = 2; i <= N; i++) {
scanf("%d", &order[i]);
}
// for (int i = 1; i <= N; i++) {
// printf(" %d", order[i]);
// }
int impossible = 0;
for (int i = 1; i <= N; i++) {
if (state[order[i]] == 2) { // 如果之前已经要求出栈,但是现在才出栈,肯定就不对
impossible = 1;
break;
}
if (state[order[i]] == 1) { // 已经是在栈里的,才可能可以出栈,比它大的要么没来要么在栈里
// 如下有判断
}
if (state[order[i]] == 0) { // 还没进栈,就必须能进栈,比它小的就要么出栈了要么在栈里
// 如下有判断
}
for (int k = order[i] + 1; k <= N; k++) { // 编号比它大的要么没来要么出栈了
if (!(state[k] == 0 || state[k] == 2)) {
impossible = 1;
break;
}
}
for (int k = 1; k < order[i]; k++) { // 编号比它小的要么出栈了要么在栈里
if (state[k] != 2) { // 编号比它小的不是出栈状态就一定在栈里
state[k] = 1;
}
if (!(state[k] == 2 || state[k] == 1)) {
impossible = 1;
// printf("???\n");
break;
}
}
if (impossible == 1) {
break;
}
state[order[i]] = 2; // 标记出栈
}
if (impossible == 1) {
printf("No\n");
} else {
printf("Yes\n");
}
}
printf("\n");
}
return 0;
}
思路二代码:
// UVa514铁轨
// 思路一:扫序列
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int MAXN = 1010;
int N;
int stack[MAXN];
int order[MAXN];
int top = -1; // 减到空,用来判空
void Push(int value) {
top++;
stack[top] = value;
return;
}
int Pop() {
int value = stack[top];
top--;
return value;
}
bool isEmpty() {
if (top == -1) {
return true;
}
return false;
}
int Top() {
if (top == -1) { // 当前栈为空
return -1;
}
int value = stack[top];
return value;
}
int main() {
freopen("data.in", "r", stdin);
while (scanf("%d", &N) == 1 && (N != 0)) {
int temp = -1;
while (scanf("%d", &temp) == 1 && (temp != 0)) {
int A = 1, B = 1;
top = -1; // make_empty
order[1] = temp;
for (int i = 2; i <= N; i++) {
scanf("%d", &order[i]);
}
// for (int i = 1; i <= N; i++) {
// printf(" %d", order[i]);
// }
while (B <= N) {
if (A == order[B]) {
A++;
B++;
} else if (Top() == order[B]) {
Pop();
B++;
} else if (A <= N) {
Push(A);
A++;
} else { // A走完了
break;
}
}
// printf("%d\n", B);
if (B != (N + 1)) {
printf("No\n");
} else {
printf("Yes\n");
}
}
printf("\n");
}
return 0;
}