[bzoj 1019] [SHOI2008]汉诺塔

n个盘子摞在柱子A上, 按照下述策略移动: - 给六种操作 (AB、AC、BA、BC、CA和CB) 赋予不同的优先级. - 每次选择符合以下两个条件的操作来移动盘子, 直到所有盘子都从A移到另一根柱子: 1. 它是所有合法操作中优先级最高的. 2. 所要移动的盘子不是上一次操作所移动的.

现在给定n和每种盘子的优先级, 求游戏终止所需要的步骤数. (1≤n≤30) 看到题面这段不明觉厉的描述......我写了个暴力, 发现对于每种优先级序列, n可以用一个二阶递推来拟合. 进而发现只有3种不同的递推式. 猜出通项. 然后交了下面这段代码:

typedef long long ll;
typedef pair<int, int> P;

stack<int> a[3];
P p[6];

ll simulate(int n)
{
	per (i, n, 1) a[0].push(i);
	
	int last = -1;
	ll c = 0;

	while ((int)a[1].size() != n && (int)a[2].size() != n) {
		rep (i, 0, 6) {
			stack<int>& fr = a[p[i].first], & to = a[p[i].second];
			int t;
			if (p[i].first != last
				&& !fr.empty()
				&& (to.empty() || to.top() > fr.top())) {
				t = fr.top();
				fr.pop();
				to.push(t);
				last = p[i].second;
				break;
			}
		}
		++c;
	}

	return c;
}

inline ll pow3(int n)
{
	ll x = 1;
	while (n--) x *= 3;
	return x;
}

int main()
{
	int n;
	scanf("%d", &n);
	rep (i, 0, 6) {
		char s[5];
		scanf("%s", s);
		p[i] = P(s[0] - 'A', s[1] - 'A');
	}
	ll t = simulate(3), ans;
	if (t == 17)
		ans = 2 * pow3(n-1) - 1;
	else if (t == 9)
		ans = pow3(n-1);
	else
		ans = (1LL<<n) - 1;

	printf("%lld\n", ans);
	return 0;
}

看题解, 正解是递推......应该很显然的啊......TAT

是把 个盘子从 移到 的步骤数, 递推式和汉诺塔的经典策略是一样的:

时的边界值, 视优先级而定. 高优先级覆盖低优先级. 比如, 如果AB的优先级高于AC, 则 , .

为什么呢? 把 个盘子从 移到 . 考虑最大的一个盘子. 它移动到 时, 另外 个盘子必须在 上, 这一步最后移动的必然是最小的盘子. 现在, 符合题面中两个条件的唯一操作是 , 也就是移动最大的盘子. 接着, 得把 个小盘子移动到 上. 步骤数是 , 不受最大盘子的影响. 注意到, 这段叙述与优先级具体是什么无关.