Problem:
MathJax.Hub.Config({ tex2jax: { inlineMath: [["$","$"], ["\\(","\\)"]], processEscapes: true }});

プログラム停止判定

皆さんは、苦労して作ったプログラムを実行してみたら、無限ループになってしまった経験はありませんか？ プログラムの実行が停止するかどうかを、実行しなくても事前に判定できると便利ですよね。

残念ながら、皆さんがふだん使っているプログラミング言語では、あらゆるプログラムに対してそのような判定をすることは不可能です。しかし、それよりもはるかに計算能力の低いプログラミング言語なら、その言語で書いたプログラムが停止するかどうかを判定するプログラムを書ける場合があります。

TinyPowerというプログラミング言語を考えます。この言語のプログラムは行の並びです。プログラムの各行には、先頭に行番号を書き、その後ろに文を一つ書きます。この言語で書ける文の種類は以下の通りです。

文の種類

動作

ADD var1 var2 var3

変数 var2 の値と var3 の値を加算した結果を変数 var1に代入する

ADD var1 var2 con

変数 var2 の値と定数 con を加算した結果を変数 var1 に代入する

SUB var1 var2 var3

変数 var2 の値から var3 の値を減算した結果を変数 var1 に代入する

SUB var1 var2 con

変数 var2 の値から定数 con を減算した結果を変数 var1 に代入する

SET var1 var2

変数 var2の値を変数 var1 に代入する

SET var1 con

定数 con を変数 var1 に代入する

IF var1 dest

変数 var1 の値が０でないときだけ、行番号 dest にジャンプする

HALT

プログラムを停止させる

行番号は正の整数で、プログラム中に同じ行番号が２つ以上現れることはありません。変数は英小文字一文字で表し、定数と変数の値は整数です。変数の宣言は不要で、変数の初期値は０です。

プログラムの実行は先頭の文から始まり、並んでいる順に文が実行されます。ただし、上の表に書かれたように、IF文の変数の値が０でないときは、変数の後ろに書かれた行番号で指定される行にジャンプし、その行に書かれた文から実行を続けます。プログラムは以下のときに停止します。

HALT文を実行したとき。

負の整数または１６以上の整数を変数に代入しようとしたとき（変数の値は更新されない）。

プログラムに現れない行番号にジャンプしようとしたとき。

プログラムの最後の文を実行した後、そこからどの行にもジャンプしないとき。

TinyPowerのプログラムが与えられたとき、それが停まるかどうかを判定するプログラムを作成せよ。

Input

入力は以下の形式で与えられる。

N
stmt1
stmt2
:
stmtN

１行目にプログラムの行数 N (1 ≤ N ≤ 50) が与えられる。続く N 行に、TinyPowerプログラムの文 stmti が与えられる。stmti は、以下のいずれかの形式で与えられる。

line ADD var1 var2 var3

または

line ADD var1 var2 con

または

line SUB var1 var2 var3

または

line SUB var1 var2 con

または

line SET var1 var2

または

line SET var1 con

または

line IF var1 dest

または

line HALT

line, dest (1 ≤ line, dest ≤ 1000) は行番号、varj (英小文字１文字)は変数、con (0 ≤ con ≤ 15) は定数を表す。stmti 中の区切りは空白１文字とする。なお、プログラム中に変数は必ず１つ以上現れ、異なる変数名は５つまでしか現れないものとする。

Output

プログラムが停止するときは、プログラムに現れる変数の結果を、変数名の辞書順に改行区切りで出力し、停止しないときは「inf」を出力する。変数の結果は、変数名と変数の値を「=」で区切って出力する。

Sample Input 1

6
10 SET c 1
20 SET i 5
100 ADD s s i
110 SUB i i c
120 IF i 100
200 HALT

Sample Output 1

c=1
i=0
s=15

入力例１は、１から５までの整数の和を計算し、その結果を変数sに格納したあと、HALT文の実行で停止する。

Sample Input 2

3
10 SET c 1
120 IF c 10
20 HALT

Sample Output 2

inf

入力例２は、行番号10でcに1を代入し、次の行番号120のIF文で行番号10に戻ることを繰り返すので、停止しない。

Sample Input 3

3
111 SET c 1
12 SUB c c 2
777 SET a 4

Sample Output 3

a=0
c=1

入力例３は、行番号111でcに1を代入し、次の行番号12でcに-1を代入しようとするので、停止する。このときcの値は-1に更新されない。行番号777は実行されないので、aの値は初期値0のままである。

Solution in C (judge verdict: Accepted)

// AOJ 0330: Halting Problem
// 2017.10.25 bal4u@uu

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define ADDV	1
#define ADDC	2
#define SUBV	3
#define SUBC	4
#define SETV	5
#define SETC	6
#define IFV		7
#define HALT	8

typedef struct { int lin, cmd, v1, v2, v3, con, to; } T;
T pgm[52]; int n;

int tr['z'+1];
char var[26]; int sz;
int lin[1002];
char tmp[110][26];
char mk[51][16][16][16][16][16];

int check(int v)
{
	if (v < 0 || v >= 16) return 0;
	return 1;
}

int varid(char x)
{
	int r;
	if (tr[x] < 0) r = sz++, tr[x] = r;
	else r = tr[x];
	return r;
}

int main()
{
	int i, pc, cnt;
	char cmd, b1[10], b2[10], b3[10];
	T *tp;

	memset(tr, -1, sizeof(tr)), memset(lin, -1, sizeof(lin)), sz = 0;
	scanf("%d", &n);
	for (tp = pgm, i = 0; i < n; i++, tp++) {
		scanf("%d%s", &tp->lin, b1); lin[tp->lin] = i, cmd = *(b1+1);
		if (cmd == 'A') tp->cmd = HALT;
		else if (cmd == 'D' || cmd == 'U') {
			scanf("%s%s%s", b1, b2, b3);
			tp->v1 = varid(*b1), tp->v2 = varid(*b2);
			if (*b3 >= 'a') {
				tp->cmd = (cmd == 'D') ? ADDV : SUBV;
				tp->v3 = varid(*b3);
			} else {
				tp->cmd = (cmd == 'D') ? ADDC : SUBC;
				tp->con = atoi(b3);
			}
		} else if (cmd == 'E') {
			scanf("%s%s", b1, b2);
			tp->v1 = varid(*b1);
			if (*b2 >= 'a') {
				tp->cmd = SETV;
				tp->v2 = varid(*b2);
			} else {
				tp->cmd = SETC;
				tp->con = atoi(b2);
			}
		} else {		// IF
			scanf("%s%d", b1, &tp->to);
			tp->cmd = IFV, tp->v1 = varid(*b1);
		}
	}
	pc = 0, cnt = 0;
	while (1) {
		if (mk[pc][var[0]][var[1]][var[2]][var[3]][var[4]]) { puts("inf"); return 0; }
		mk[pc][var[0]][var[1]][var[2]][var[3]][var[4]] = 1;
		tp = pgm+pc;
		if (tp->cmd == ADDV) {
			if (!check(var[tp->v2] + var[tp->v3])) break;
			var[tp->v1] = var[tp->v2] + var[tp->v3];
		} else if (tp->cmd == ADDC) {
			if (!check(var[tp->v2] + tp->con)) break;
			var[tp->v1] = var[tp->v2] + tp->con;
		} else if (tp->cmd == SUBV) {
			if (!check(var[tp->v2] - var[tp->v3])) break;
			var[tp->v1] = var[tp->v2] - var[tp->v3];
		} else if (tp->cmd == SUBC) {
			if (!check(var[tp->v2] - tp->con)) break;
			var[tp->v1] = var[tp->v2] - tp->con;
		} else if (tp->cmd == SETV) {
			if (!check(var[tp->v2])) break;
			var[tp->v1] = var[tp->v2];
		} else if (tp->cmd == SETC) {
			if (!check(tp->con)) break;
			var[tp->v1] = tp->con;
		} else if (tp->cmd == HALT) break;
		
		if (tp->cmd == IFV && var[tp->v1] != 0) {
			if (lin[tp->to] < 0) break;
			pc = lin[tp->to];
		} else {
			pc++;
			if (pc == n) break;
		}
	}
	for (i = 'a'; i <= 'z'; i++) if (tr[i] >= 0) printf("%c=%d\n", i, var[tr[i]]);
	return 0;
}