一名苦逼的OIer,想成为ACMer

Iowa_Battleship

洛谷1242 新汉诺塔

原题链接

显然要先把较大的盘放到目标位置。
有一个很明显的贪心方案:
设当前要把第\(x\)大的盘子从\(A\)移到\(B\),则先把比\(x\)小的盘子全部移到\(C\)柱,再将第\(x\)大的盘子移到\(B\)柱,这样递归求解。
该种贪心方案在大多数情况都是最优的,但是有个大佬出了一个\(HACK\)数据,将该贪心方案\(HACK\)掉了。
以下是该数据

//输入
3
1 3
0
2 2 1
2 2 1
0
1 3
//正确输出
move 3 from A to B
move 1 from C to B
move 2 from C to A
move 1 from B to A
move 3 from B to C
5
//该贪心方案的错误输出
move 1 from C to A
move 2 from C to B
move 1 from A to B
move 3 from A to C
move 1 from B to C
move 2 from B to A
move 1 from C to A
7

这时,我们就需要考虑另一种可能为最优的方案:
设当前要把第\(x\)大的盘子从\(A\)移到\(B\),则先把比\(x\)小的盘子全部移到\(B\)上,再将\(x\)盘移到\(C\)柱,再将比\(x\)小的盘子全部移到\(A\)上,最后将\(x\)盘移到\(B\)上。
虽然在大多数情况都是第一种更优,但是当比\(x\)小的盘子已经全部在\(x\)盘的目标柱上,这时可能是第二种方案更优。
由于该种特殊情况只可能出现在第一步(因为第一步已经将比最大盘小的盘都移到一根柱上了),所以我们可以复制一遍数组,用第二种方案跑第一步,再用第一种方案接着跑,最后和全部用第一种方案取最小值输出即可。
这题使用递归来写会更简洁一点(不过我复制一遍函数、数组名显得很长很臭)。

#include<cstdio>
using namespace std;
const int N = 50;
struct dd{
	int x;
	char f, t;
};
dd an[N << 10], b_an[N << 10];
int a[N], b[N], b_a[N], b_b[N], s, b_s, k, b_k;
inline int re()
{
	int x = 0;
	char c = getchar();
	bool p = 0;
	for (; c < '0' || c > '9'; c = getchar())
		p |= c == '-';
	for (; c >= '0' && c <= '9'; c = getchar())
		x = x * 10 + c - '0';
	return p ? -x : x;
}
void dfs(int x ,int y)
{
	if (!(a[x] ^ y))
		return;
	for (int i = x - 1; i; i--)
		dfs(i, 6 - a[x] - y);
	an[++k].x = x;
	an[k].f = a[x] + 'A' - 1;
	an[k].t = y + 'A' - 1;
	a[x] = y;
	s++;
}
void b_dfs(int x ,int y)
{
	if (!(b_a[x] ^ y))
		return;
	for (int i = x - 1; i; i--)
		b_dfs(i, 6 - b_a[x] - y);
	b_an[++b_k].x = x;
	b_an[b_k].f = b_a[x] + 'A' - 1;
	b_an[b_k].t = y + 'A' - 1;
	b_a[x] = y;
	b_s++;
}
void pr(int o, dd ans[])
{
	for (int i = 1; i <= o; i++)
		printf("move %d from %c to %c\n", ans[i].x, ans[i].f, ans[i].t);
}
int main()
{
	int i, j, n, m, x;
	n = re();
	for (i = 1; i < 4; i++)
		for (m = re(), j = 1; j <= m; j++)
		{
			x = re();
			a[x] = b_a[x] = i;
		}
	for (i = 1; i < 4; i++)
		for (m = re(), j = 1; j <= m; j++)
		{
			x = re();
			b[x] = b_b[x] = i;
		}
	for (i = n - 1; i; i--)//模拟用第二种方案跑第一步
		b_dfs(i, b_b[n]);
	x = b_a[n];
	b_dfs(n, 6 - b_a[n] - b_b[n]);
	for (i = n - 1; i; i--)
		b_dfs(i, x);
	b_dfs(n, b_b[n]);
	for (i = n - 1; i; i--)//接着全部用第一种方案
		b_dfs(i, b_b[i]);
	for (i = n; i; i--)//全部用第一种方案跑
		dfs(i, b[i]);
	pr(s > b_s ? b_k : k, s > b_s ? b_an : an);
	printf("%d", s > b_s ? b_s : s);
	return 0;
}

posted on 2018-10-19 20:15  Iowa_Battleship  阅读(162)  评论(0编辑  收藏  举报

导航