002:拨钟问题

描述

有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这题的测试点不全。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值