枚举-拨钟问题
来源:互联网 发布:windows macosx u盘 编辑:程序博客网 时间:2024/04/29 16:18
问题描述:
有9个时钟,排成一个3*3矩阵。
现在需要用最少的移动,将9个时钟的指针都拨到12点的位置。共允许有9种不同的移动。如下表所示,每个移动会将若干个时钟的指针沿顺时针方向拨动90度。
- 输入:
- 9个整数,表示各时钟指针的起始位置,相邻两个整数之间用单个空格隔开。其中,0=12点、1=3点、2=6点、3=9点。
- 输出:
- 输出一个最短的移动序列,使得9个时钟的指针都指向12点。按照移动的序号从小到大输出结果。相邻两个整数之间用单个空格隔开。
3 3 0
2 2 2
2 1 2
样例输出:
4 5 8 9
分析:
如果存在某个局部,如果这个局部的状态被确定,纳闷其余部分的状态只能是确定的一种或不多的n种,纳闷就只需枚举这个局部的状态即可。
假设时钟指针位置对应的值为clock_time,那么顺时针旋转90°就是clock_time = (clock_time+1)%4
这一组时针就用一个数组表示。9种操作对应一个二维数组。
这一题实质类似熄灯问题和画家问题。其共通点在于:
操作对环境的改变是无序的,每个操作都会影响到周围的状态。
同时每一种操作都有周期性限制,也即最多需要几次操作,多于这个次数产生循环。
熄灯问题中,每个灯最多熄灯一次,因为灯只有两种状态,
并且循环。而这里,有4种循环的状态,因此每个移动操作顶多使用3次。
我们对移动方法1,2,3进行枚举,每种方法无非实施0-3次,也即一共4^3=64种情况。
这些情况之间并非没有关系。
例如,我们确定了1,2,3的情况数,那么得到一个灯A,B,C的状态,而只有移动4能够改变A,
移动5能够改变B,移动6能够改变C,那么移动4-6的次数也确定了。
同样,这时只有移动7能够改变D,移动9能够改变F,这时移动7和9的次数也确定了。
最后,时钟A,B,C,D,F都已经到达12点,E,G,H,I还没确定,只剩下移动8能够改变GHI,
所以只要检查E是否已经到达12点以及,GHI的时钟数是否相等就行了。
最后找到一个移动次数最小的情况。
这题也可以用暴力搜索,因为最多有4^9个组合,不会超时。
这题还可以列出一个方程组,九个未知数,通过高斯消元法来解方程组。
源码:
#include <iostream>#include <vector>#include <algorithm>#include <cstring>using namespace std;void operate(unsigned operations[10][10], unsigned clocks2[10], int op_num, int op_count){ for (int i = 1; i <= 9; i++) { clocks2[i] += operations[op_num][i]*op_count; clocks2[i] %= 4; }}int main(){ //保存原始的时钟状态 unsigned clocks[10] = {0}; //保存最后最少的移动次数,最多也就是27次 int min_res = 28, tmp = 0; //保存最小次数时候的移动方法 unsigned min_op[10] = {0}; //9种操作对应数组 unsigned operations[10][10] = { {0}, // A B C D E F G H I {0, 1, 1, 0, 1, 1, 0, 0, 0, 0}, //op1: ABDE {0, 1, 1, 1, 0, 0, 0, 0, 0, 0}, //op2: ABC {0, 0, 1, 1, 0, 1, 1, 0, 0, 0}, //op3: BCEF {0, 1, 0, 0, 1, 0, 0, 1, 0, 0}, //op4: ADG {0, 0, 1, 0, 1, 1, 1, 0, 1, 0}, //op5: BDEFH {0, 0, 0, 1, 0, 0, 1, 0, 0, 1}, //op6: CFI {0, 0, 0, 0, 1, 1, 0, 1, 1, 0}, //op7: DEGH {0, 0, 0, 0, 0, 0, 0, 1, 1, 1}, //op8: GHI {0, 0, 0, 0, 0, 1, 1, 0, 1, 1} //op9: EFHI }; //移动操作改变数据clocks2,而clocks备份源数据 unsigned clocks2[10] = {0}; //记录每种移动方法操作次数 unsigned op[10] = {0}; //保存枚举移动1,2,3的64种执行情况 unsigned move123[64][3]; unsigned num, divide_num; for (int i = 0; i < 64; i++) { num = i; //将10进制i转换为对应的4进制move123[i] for (int j = 2; j >= 0; j--) { divide_num = (unsigned)num/4; move123[i][j] = num - divide_num*4; num = divide_num; } } for (int i = 1; i <= 9; i++) cin >> clocks[i]; //枚举移动1,2,3的64种执行次数,计算出移动4-9的移动次数,判断是否满足最后条件,记录总次数 for (int i = 0; i < 64; i++) { memcpy(clocks2, clocks, sizeof(unsigned)*10); memset(op, 0, sizeof(unsigned)*10); for (int j = 1; j <=3; j++) { op[j] = move123[i][3-j]; operate(operations, clocks2, j, op[j]); } //移动操作4,5,6的次数分别由时钟A,B,C的状态决定 for (int j = 4; j <=6; j++) { op[j] = (4-clocks2[j-3])%4; operate(operations, clocks2, j, op[j]); } //移动操作7,9的次数分别由时钟D,F的状态决定 op[7] = (4-clocks2[4])%4; operate(operations, clocks2, 7, op[7]); op[9] = (4-clocks2[6])%4; operate(operations, clocks2, 9, op[9]); //判断E是否为0,GHI是否相等 if (clocks2[5] == 0 && clocks2[7] == clocks2[8] && clocks2[8] == clocks2[9]) { op[8] = (4-clocks2[7])%4; tmp = 0; for (int j = 1; j <= 9; j++) tmp += op[j]; if (tmp < min_res) { min_res = tmp; memcpy(min_op, op, sizeof(unsigned)*10); } } } //非零项排序 vector<unsigned> res; for (int i = 1; i <= 9; i++) { if (min_op[i] != 0) { res.push_back(i); min_op[i]--; i--; } } sort(res.begin(), res.end()); for (vector<unsigned>::iterator it = res.begin(); it != res.end(); it++) cout << *it << ' '; cout << "\n"; return 0;}
- 枚举-拨钟问题
- 枚举-拨钟问题
- 【贪心枚举】拨钟问题
- poj 2814 拨钟问题(枚举)
- 画家问题和拨钟问题 枚举法 举例
- 枚举-拨钟问题(算法基础 第2周)
- poj2814-拨钟问题-C语言-枚举算法
- 拨钟问题pku1166
- 拨钟问题
- 拨钟问题
- Openjudge2814:拨钟问题
- openjudge-拨钟问题
- 拨钟问题
- 拨钟问题
- 拨钟问题
- C语言中的拨钟问题(枚举)(暴力搜索)POJ1166
- poj 1166 拨钟问题
- POJ-1816 拨钟问题
- SaltStack
- 文章标题
- 一个老迅雷人眼中的“迅雷恩怨”门: 无论对错, 至少良知不可逝
- VR开发——Unity动画系统2(V客it学院技术分享)
- 微博的创建发布(js简单代码)
- 枚举-拨钟问题
- 十个生成模型(GANs)的最佳案例和原理
- iOS开发UITableViewCell右边的原生图标设置
- maven:多源文件夹生成javadoc
- ubuntu下用conda安装opencv
- 手把手教你如何利用Meterpreter渗透Windows系统
- Tomcat安装与配置
- cmake快速入门网页材料,两个网页就够了
- Python 2.6.2的字节码指令集一览