来自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];
}
}