思路分析
构造排列表
立方体只有 \(4\) 个,暴力法是可行的。但是如果我们要暴力,首先得清楚一个立方体到底有几种不同的旋转方式。
接下来,我们用“姿态”一词代替“旋转方法”。假设 \(6\) 个面的编号为 \(1\sim6\),从中选择一个面作为“顶面”,“顶面”的对面为“底面”。然后我们在剩下来的 \(4\) 个面中选一个作为“正面”,则其他面都可以唯一确定,因此有 \(4\times6=24\) 种姿态。
在代码中,每一种姿态对应一个全排列 \(P\)。其中,\(P_i\) 代表编号 \(i\) 所在的位置。
接下来,我们可以用代码来实现排列表,详见代码实现一栏。
暴力出奇迹
我们应该如何暴力呢?一种方法是枚举最后那个“相同的立方体”,然后对于每个立方体,看看哪种姿态需要重新涂色的面最少。但由于 \(4\) 个立方体可能会有 \(24\) 种不同的颜色,需要枚举 \(24^6\) 种情况,有超时的风险。
另一种方法是先枚举每个立方体的姿态(第一个作为“参考系”,不用旋转),然后对于 \(6\) 个面,分别需哪一个出现次数最多的颜色作为“标准”,和它不同的颜色一律重涂。由于每个立方体的姿态有 \(24\) 种,而需要枚举的有 \(3\) 个立方体,所以我们只需要枚举 \(24^3\) 种情况,稳打稳能通过这道题。
代码实现
// 构造排列表代码
#include <cstdio>
#include <cstring>
using namespace std;
int left[] = {4, 0, 2, 3, 5, 1};
int up[] = {2, 1, 5, 0, 4, 3};
// 按照排列T旋转姿态p
void rot(int *T, int *p) {
int q[6];
memcpy(q, p, sizeof(q));
for (int i = 0; i < 6; i++) p[i] = T[q[i]];
}
void solve() {
int p0[6] = {0, 1, 2, 3, 4, 5};
printf("int dice[24][6] = {\n");
for (int i = 0; i < 6; i++) {
int p[6];
memcpy(p, p0, sizeof(p0));
switch (i) {
case 0:
rot(up, p);
break;
case 1:
rot(left, p);
rot(up, p);
break;
case 3:
rot(up, p);
rot(up, p);
break;
case 4:
rot(left, p);
rot(left, p);
rot(up, p);
break;
case 5:
rot(left, p);
rot(left, p);
rot(left, p);
rot(up, p);
break;
}
for (int j = 0; j < 4; j++) {
printf("{%d, %d, %d, %d, %d, %d},\n", p[0], p[1], p[2], p[3], p[4], p[5]);
rot(left, p);
}
}
printf("};\n");
}
int main() {
solve();
return 0;
}
// 最终代码
#include <bits/stdc++.h>
using namespace std;
const int maxn = 4;
const int dice24[24][6] = { // 通过上一份代码生成的排列表
{2, 1, 5, 0, 4, 3},
{2, 0, 1, 4, 5, 3},
{2, 4, 0, 5, 1, 3},
{2, 5, 4, 1, 0, 3},
{4, 2, 5, 0, 3, 1},
{5, 2, 1, 4, 3, 0},
{1, 2, 0, 5, 3, 4},
{0, 2, 4, 1, 3, 5},
{0, 1, 2, 3, 4, 5},
{4, 0, 2, 3, 5, 1},
{5, 4, 2, 3, 1, 0},
{1, 5, 2, 3, 0, 4},
{5, 1, 3, 2, 4, 0},
{1, 0, 3, 2, 5, 4},
{0, 4, 3, 2, 1, 5},
{4, 5, 3, 2, 0, 1},
{3, 4, 5, 0, 1, 2},
{3, 5, 1, 4, 0, 2},
{3, 1, 0, 5, 4, 2},
{3, 0, 4, 1, 5, 2},
{1, 3, 5, 0, 2, 4},
{0, 3, 1, 4, 2, 5},
{4, 3, 0, 5, 2, 1},
{5, 3, 4, 1, 2, 0},
};
int n, dice[maxn][6], ans;
int r[maxn], color[maxn][6];
vector<string> names;
int ID(const char *name) {
string s(name);
size_t len = names.size();
for (size_t i = 0; i < len; i++)
if (names[i] == s) return i;
names.push_back(s);
return len;
}
void check() {
for (int i = 0; i < n; i++)
for (int j = 0; j < 6; j++)
color[i][dice24[r[i]][j]] = dice[i][j];
int tot = 0; // 需要重新涂色面数
for (int j = 0; j < 6; j++) { // 考虑每个面
int cnt[maxn * 6], maxface = 0; // cnt表示每种颜色出现的次数
memset(cnt, 0, sizeof(cnt));
for (int i = 0; i < n; i++)
maxface = max(maxface, ++cnt[color[i][j]]);
tot += n - maxface;
}
ans = min(ans, tot);
}
void dfs(int d) {
if (d == n) {
check();
return ;
}
for (int i = 0; i < 24; i++) {
r[d] = i;
dfs(d + 1);
}
}
int main() {
while (scanf("%d", &n) == 1 && n) {
names.clear();
for (int i = 0; i < n; i++)
for (int j = 0; j < 6; j++) {
char name[30];
scanf("%s", name);
dice[i][j] = ID(name);
}
ans = n * 6; // 最终值的上界
r[0] = 0; // 第一个立方体不用旋转
dfs(1);
printf("%d\n", ans);
}
return 0;
}
标签:24,UVA1352,题解,++,int,立方体,rot,left
From: https://www.cnblogs.com/IShallReturn/p/UVA1352-ti-jie.html