描述
有9个时钟,排成一个3*3的矩阵。
|-------| |-------| |-------| | | | | | | | |---O | |---O | | O | | | | | | | |-------| |-------| |-------| A B C |-------| |-------| |-------| | | | | | | | O | | O | | O | | | | | | | | | | |-------| |-------| |-------| D E F |-------| |-------| |-------| | | | | | | | O | | O---| | O | | | | | | | | | |-------| |-------| |-------| G H I (图 1)现在需要用最少的移动,将9个时钟的指针都拨到12点的位置。共允许有9种不同的移动。如下表所示,每个移动会将若干个时钟的指针沿顺时针方向拨动90度。
移动 影响的时钟 1 ABDE 2 ABC 3 BCEF 4 ADG 5 BDEFH 6 CFI 7 DEGH 8 GHI 9 EFHI输入
9个整数,表示各时钟指针的起始位置,相邻两个整数之间用单个空格隔开。其中,0=12点、1=3点、2=6点、3=9点。
输出
输出一个最短的移动序列,使得9个时钟的指针都指向12点。按照移动的序号从小到大输出结果。相邻两个整数之间用单个空格隔开。
样例输入
3 3 0 2 2 2 2 1 2样例输出
4 5 8 9
可以看做是把上一道按钮的题变了一下,状态数从2变到了4(拨动时钟4次后时钟会回到最开始的状态,相当于没有移动),同时也给了一系列指定效果的操作。
首先可以暴力枚举,每种移动方法只有使用0次、1次、2次和3次这四种情况,一共也就是4^9种情况。
不过本题实际上可以像按钮问题(熄灯问题)一样找到枚举局部的方法。经过观察发现,我们确定了操作1、2、3的次数后,A、B、C三个钟就只能分别被4、5、6操作改变了,因此也就可以确定4、5、6应该进行几次。同理可再确定7、9(看D、F或D、E),然后确定8(看G或H或I),最后检查G、H、I是否正确。
这里只给出局部枚举的实现。
#include <cstdio>
#include <iostream>
#include <cstring>
using namespace std;
int Clock[9], OriginClock[9]; //前者是操作对象,后者是原件
int times[10]; //从1~9为各项操作次数
enum Clock_index
{
A,
B,
C,
D,
E,
F,
G,
H,
I
};
//九种不同的操作的效果
void step1(int i)
{
Clock[A] = (Clock[A] + i) % 4;
Clock[B] = (Clock[B] + i) % 4;
Clock[D] = (Clock[D] + i) % 4;
Clock[E] = (Clock[E] + i) % 4;
}
void step2(int i)
{
Clock[A] = (Clock[A] + i) % 4;
Clock[B] = (Clock[B] + i) % 4;
Clock[C] = (Clock[C] + i) % 4;
}
void step3(int i)
{
Clock[C] = (Clock[C] + i) % 4;
Clock[B] = (Clock[B] + i) % 4;
Clock[F] = (Clock[F] + i) % 4;
Clock[E] = (Clock[E] + i) % 4;
}
void step4(int i)
{
Clock[A] = (Clock[A] + i) % 4;
Clock[D] = (Clock[D] + i) % 4;
Clock[G] = (Clock[G] + i) % 4;
}
void step5(int i)
{
Clock[H] = (Clock[H] + i) % 4;
Clock[B] = (Clock[B] + i) % 4;
Clock[F] = (Clock[F] + i) % 4;
Clock[D] = (Clock[D] + i) % 4;
Clock[E] = (Clock[E] + i) % 4;
}
void step6(int i)
{
Clock[C] = (Clock[C] + i) % 4;
Clock[F] = (Clock[F] + i) % 4;
Clock[I] = (Clock[I] + i) % 4;
}
void step7(int i)
{
Clock[G] = (Clock[G] + i) % 4;
Clock[H] = (Clock[H] + i) % 4;
Clock[D] = (Clock[D] + i) % 4;
Clock[E] = (Clock[E] + i) % 4;
}
void step8(int i)
{
Clock[G] = (Clock[G] + i) % 4;
Clock[H] = (Clock[H] + i) % 4;
Clock[I] = (Clock[I] + i) % 4;
}
void step9(int i)
{
Clock[I] = (Clock[I] + i) % 4;
Clock[H] = (Clock[H] + i) % 4;
Clock[F] = (Clock[F] + i) % 4;
Clock[E] = (Clock[E] + i) % 4;
}
void Print()
{
for (int i = 1; i <= 9; i++)
for (int j = 1; j <= times[i]; j++)
printf("%d ", i);
}
int main()
{
//输入初始位置
for (int i = 0; i < 9; i++)
cin >> OriginClock[i];
//开始局部枚举
for (times[1] = 0; times[1] <= 3; times[1]++)
{
for (times[2] = 0; times[2] <= 3; times[2]++)
{
for (times[3] = 0; times[3] <= 3; times[3]++)
{
for (int i = 0; i < 9; i++)
Clock[i] = OriginClock[i];
step1(times[1]);
step2(times[2]);
step3(times[3]);
//根据A、B、C确定4、5、6
times[4] = (4 - Clock[A]) % 4;
step4(times[4]);
times[5] = (4 - Clock[B]) % 4;
step5(times[5]);
times[6] = (4 - Clock[C]) % 4;
step6(times[6]);
//根据D、F确定7、9
times[7] = (4 - Clock[D]) % 4;
step7(times[7]);
times[9] = (4 - Clock[F]) % 4;
step9(times[9]);
//检查,到这里时应该E调好了,G、H、I同步;此二者是最终能调好的充要条件
if (Clock[E] == 0 && Clock[G] == Clock[H] && Clock[G] == Clock[I])
{
times[8] = (4 - Clock[G]) % 4;
goto success;
}
}
}
}
success:
Print();
return 0;
}
上述代码成功AC,但是这样就能说明它是对的了吗?实际上我觉的是错的。它找到的不一定是操作数最小的方案。事实上,如果4 4 4 4 4和1 1 1都能调好钟的话,程序会优先枚举到前者,从而答案错误。因此我觉得POJ这题的测试点不全。