紫书刷题进行中,题解系列点这里
习题4-4 UVA253 Cube painting(29行AC代码)
题目大意
现有固定编号(1~6)的立方体,给定两个上色序列A,B(仅由红蓝绿三种颜色构成),问A能否通过x,y,z三个旋转轴旋转得到B(不考虑镜像,即轴对称)
思路分析
有很多人通过两个正方体的3组对立面是否一致来判断结果,其实有bug,可如下构造反例:
- 构造A:rbg各出现两次,且对立面均不同色
- 构造B:选择一个对立面交换颜色
grbrgbggbrrb // false, 仅判断3组对立面相等的反例
因此,可通过暴力枚举,解决问题,但还需多一些思考和推导,可得到结论1:
结论1:若正方体A,B上色方案相同,那么A必定可在3次旋转得到B
- 旋转轴共3个(x,y,z),每个轴共有4个旋转角度(0,90,180,270)
可将三个旋转轴看成空间基向量,任意的向量均可由基向量构造出来,其中旋转角度就是基向量前的常数
结论2:B=d1*x+d2*y+d3*z // 类似这种表达,(d1,d2,d3)表示绕每个轴旋转角度
根据结论1可知,旋转组合顺序与结果无关,4*4*4=64种情况必定包含了所有的旋转结果,因此,对于每个判断,仅需枚举64种旋转即可。
算法设计
对于旋转,可用一个轮换来模拟(类似约瑟夫环过程),以下分别表示绕3个轴转动时会轮换改变的序列
int a[3][4] = {{2,4,5,3}, {2,6,5,1}, {1,4,6,3}}; // 3个旋转轴对应改变的轮换序列
用string存储上色序列,定义旋转函数rotate
,表示正方体绕i轴旋转j*90度。
void rotate(string& sr, int i, int j) { // sr绕i轴旋转j*90度
string s = sr;
for (int k = 0; k <= 3; k ++) { // 轮换:模拟旋转
sr[a[i][k]-1] = s[a[i][(j+k)%4]-1]; // 从0开始存储,正方体标号从1开始
}
}
测试用例设计
rbgggrrggbgr // True, 1轴旋转
rrrbbbrrbbbr // false
rbgrbgrrrrrg // false
rbgggrrgggbr // true,全等2
rggbgrrgggbr // true,
gggggggggggg // true,全等1
rggbgrggrrgb // True,2轴旋转
rggbgrggrrbg // True,3轴旋转
ggrrgbggrrbg // True, 1轴旋转
rbgggrgrbgrg // True, 3轴4次旋转
rbgggrgbrrgg // True, 3轴4次旋转
grbrgbggbrrb // false, 仅判断3组对立面相等的反例
AC代码(C++11,判定条件,中级模拟,推导思考,轮换)
#include<bits/stdc++.h>
using namespace std;
int a[3][4] = {{2,4,5,3}, {2,6,5,1}, {1,4,6,3}}; // 3个旋转轴对应改变的轮换序列
string sa, sb, st;
void rotate(string& sr, int i, int j) { // sr绕i轴旋转j*90度
string s = sr;
for (int k = 0; k <= 3; k ++) { // 轮换:模拟旋转
sr[a[i][k]-1] = s[a[i][(j+k)%4]-1]; // 从0开始存储,正方体标号从1开始
}
}
int main() {
while (cin >>st) {
sa = st.substr(0,6); sb = st.substr(6);
bool isSame = false; // 判断是否存在一样的情况
for (int i = 0; i < 4 && !isSame; i ++) { // x轴4种转动角度
for (int j = 0; j < 4 && !isSame; j ++) { // y轴
for (int k = 0; k < 4 && !isSame; k ++) { // z轴
st = sb; // 更新
rotate(st, 0, i); // x轴转i*90角度
rotate(st, 1, j); // y轴转j*90角度
rotate(st, 2, k); // z轴转k*90角度
if (st == sa) isSame = true;
}
}
}
printf("%s\n", isSame ? "TRUE" : "FALSE");
}
return 0;
}