时钟同步问题

问题描述
以4×4格子形状摆放16个时钟。所有时钟的时针都指向12点、3点、6点或者9点。现在要把所有时钟的时针指向12点。修正时钟时间的唯一方式是,通过控制10个开关,且每个开关连接到少则3个、多则5个时钟上。每按1次开关,与之连接的时钟就会顺时针转动3个小时。
开关与连接的时钟有以下对应的关系。

输入
第一行输入测试用例的个数C(C≤30)。各测试用例以整数形式输入16个时钟当前指向的时间,其数值为12、3、6、9之一。
输出
每个测试用例在第1行显示出1个整数。这个整数表示,为了使所有时钟指向12点而按开关的次数。

解题思路
要直接解这道题会比较麻烦。如果利用问题的特性,加以适当简化,就可以用旁举搜索法解决。
首先要弄清,与示例输入输出说明不同,其实按开关的顺序并不重要。即使调换按两个开关的顺序,也不会给结果带来任何变化。因此,最终要计算的只是共按下了几次开关而已。
变换到此,也不能马上适用穷举搜索法解题。想要利用穷举搜索法,必须列出按开关的所有组合。可是按同一开关的次数又没有限制,所以这种组合变得无穷多。
对于这种问题,有个循环规律值得利用:时钟每过12个小时就会回到起点。利用这种循环规律,可把无限组合变换为有限组合。对于同一开关,按4次就会让时钟转动12个小时,也就等同于没有按下开关。由此得出一个结论:对任意一个开关,最多只会按下3次。那么就有,对一个开关按下的次数是0到3的整数,共有10个开关,所以组合的个数为 检索这些组合,不同实现方法的耗时会有所不同,但能够得出“可在限定时间内完成检索”的结论。从而也能判断出利用穷举搜索法即可顺利解题。

注意事项

  • 没有答案时返回的是INF。
  • 开关与时钟的对应关系保存到二维数组。
const int INF = 9999 ,SWITCHES = 10,CLOCKS = 16;
// link[i][j] = 'x',表示第i个开关与第j个时钟相连
char linked[SWITCHES][CLOCKS+1] = {
    "xx..............",
    "...x...x.x.x....",
    "....x.....x...xx",
    "x...xxxx........",
    "......xxx.x.x...",
    "x.x...........xx",
    "...x..........xx",
    "....xx.x......xx",
    ".xxxxx..........",
    "...xxx...x...x.."
};
//判断所有时钟是否都指向12点
bool areAligned(const vector<int>&clocks);

//按下第swtch 个开关
void push(vector<int>&clocks,int swtch){
    for(int cloc = 0;cloc<CLOCKS;++cloc){
        if(linked[swtch][cloc] == 'x'){
            clocks[cloc] += 3;
            if(clocks[cloc] == 15)clocks[cloc] = 3;
        }
    }
}
// clocks 当前所由时钟的状态
//swtch :当前要按的开关的序号
//此时返回 “按下剩余开关使clocks 调整为12点的最少次数
//若不可能调节为12点,则返回大于INF的数值

int solve(vector<int>&clocks,int swtch){
    if(swtch == SWITCHES) // 到了最后一个开关了
    return areAligned(clocks)?0:INF;
    //对当前开关尝试0~3次
    int ret = INF;
    for(int cnt = 0;cnt<4;++cnt){
        ret = min(ret,cnt + solve(clocks,swtch+1));
        push(clocks,swtch);//4次就相当于dfs中的回溯
    }
    // push 一共调用4次所以与原来状态一样
    return ret;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

落春只在无意间

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值