[NOI2000]程序分析机题解

来自bzoj.org/p/Z2477(传统题)

题面


TB程序的执行:

  • 程序从行号最小的一条语句开始执行,在未遇到条件语句时按行号由小至大顺序执行。
  • 所有变量在程序执行前被自动初始化为 0。
  • 累加语句将语句中变量的值加上语句中的整数送回该变量。
  • 输出语句将语句中变量的值在监视器上显示出来。
  • 执行条件语句时,当且仅当该语句中的变量与紧跟在等号后面的整数值相等,后面的转移语句才被执行。该语句中的所有整数值至多为 4 位。
  • 转移语句被执行后,程序将转去执行 GO 后面指定的行号的语句。
  • 当程序执行结束语句后,结束整个程序的执行。
  • 假设该系统能处理任意大小的整数,而不会发生溢出。

请编程,对于给定的 TB 语言程序 P,求该程序所执行的语句数(执行条件语句不论是否成功转移,仅记为执行一条语句)。

  • P 中每条语句的长度不超过 20 个字符。
  • P 中转移语句里 GO 后面的行号一定有对应的语句。
  • P 中可能有多个不同行号的结束语句。
  • P 中行号最大的语句一定是结束语句。
  • P 中的行号都不大于 3000。
  • 输入文件不一定是按行号递增顺序给出 P 的。

一道工作量特大的模拟题,如果通过一般算法进行运算,代码会非常的繁重。

思路


说白了,这道题就是给我们一个程序,让我们输出运行次数,若出现死循环,输出-1。

重要的语句无非就是累加、判断和转移

首先输入文件就很难,我们可以用一个while语句先输入行号xline,再使用getline输入指令。

然后从最大行号开始,若找到某一行有指令则把这一行的下一行设为nowline(最后一行的下一行为0,到了最后一行保证是END,不担心执行第0行语句)然后把nowline设为当前行号,最后nowline会变成最小行号。

然后,进入一个无止境的while。

如果++carryout(运行次数)大于一个极大值(例如1e7),就视为程序无法结束,输出-1,exit出去。

如果nowline代表的指令第2个字符(之后忽略0号字符,因为它是一个空格。第x个字符下标就为x)为‘N’,说明是END语句,输出运行次数carryout,exit出去。


重点:


否则的话,说明这是一个带参数的指令(输出指令除外),第二个字符会是F(IF)、O(GO)、+(增加)以及可以忽略的?(题目没要求我们执行这种语句)。

这时,我们要创建一个函数,用来查找第x行的第y个字符之后第1个出现的数。这个函数很简单,也很容易实现。

int firstnum(int line,int start)
{
	int num = 0;
	while(instruction[line][start] < 48 || instruction[line][start] > 57) start++;
	while(start < instruction[line].size() && instruction[line][start] > 47 && instruction[line][start] < 58)
	{
		num *= 10;
		num += instruction[line][start] - 48;
		start++;
	}
	return num;
}

GO语句的数字在第4个字符,只需把nowline变为firstnum(nowline,4)即可实现。

+语句的字母在第1个字符,可以直接用map访问,并加上firstnum(nowline,4),把nowline变为下一行。

IF语句的字母在第4个字符,可以直接用map访问,判定数在第6个字符之后,firstnum(nowline,6),而判定数最多结束于第9个字符,不影响从最少的情况(第11个字符为行号)。所以如果等式成立,把nowline设为firstnum(nowline,11)即可。

否则,把nowline设为下一行。


代码:


#include<iostream>
#include<cstring>
#include<map>
using namespace std;
int xline,nextins[3003],nowline,carryout;
string instruction[3003];
map <char,int> variable;
int firstnum(int line,int start)
{
	int num = 0;
	while(instruction[line][start] < 48 || instruction[line][start] > 57) start++;
	while(start < instruction[line].size() && instruction[line][start] > 47 && instruction[line][start] < 58)
	{
		num *= 10;
		num += instruction[line][start] - 48;
		start++;
	}
	return num;
}
signed main()
{
	while(cin >> xline)
	{
		getline(cin,instruction[xline]);
	}
	for(int i = 3000;i >= 0;i--) if(instruction[i] != "") nextins[i] = nowline,nowline = i;
	while(true)
	{
		if(++carryout >= 10000000) cout << "-1",exit(0);
		if(instruction[nowline][2] == 'N') cout << carryout,exit(0);
		else if(instruction[nowline][2] == '+') variable[instruction[nowline][1]] += firstnum(nowline,3),nowline = nextins[nowline];
		else if(instruction[nowline][2] == 'O') nowline = firstnum(nowline,4);
		else if(instruction[nowline][2] == 'F' && variable[instruction[nowline][4]] == firstnum(nowline,6)) nowline = firstnum(nowline,11);
		else nowline = nextins[nowline];
	}
}

  • 24
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值