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;
} 
posted @ 2021-02-06 20:45  Mo_hw  阅读(65)  评论(0编辑  收藏  举报