http://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&category=456&page=show_problem&problem=4098
题意:n个带颜色立方体,每个面一种颜色,要求重新涂尽量少的面,使得所有立方体完全相同。
思路:我们给一个立方体的每个面标上号码0-5时,每个立方体有24种不同的状态,我们通过枚举立方体的24种状态,然后在六个面的每个面选择出现次数最多的颜色,其他不同的重涂(贪心的思想)。
24种状态的生成需要使用的一段代码,也可以手动写出来不过出错几率较大。
#include<cstdio>
#include<cstring>
int left[] = {4, 0, 2, 3, 5, 1};
int up[] = {2, 1, 5, 0, 4, 3};
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 enumerate_permutations()
{
int p0[6] = {0, 1, 2, 3, 4, 5};
printf("int dice24[24][6] = {\n");
for (int i = 0; i < 6; i++)
{
int p[6];
memcpy(p, p0, sizeof(p0));
if (i == 0)
rot(up, p);
if (i == 1)
{
rot(left, p);
rot(up, p);
}
if (i == 3)
{
rot(up, p);
rot(up, p);
}
if (i == 4)
{
rot(left, p);
rot(left, p);
rot(left, p);
rot(up, p);
}
if (i == 5)
{
rot(left, p);
rot(left, p);
rot(up, p);
}
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()
{
enumerate_permutations();
return 0;
}
有了状态之后,就开始枚举贪心了,因为题目n是不大于4的,所以枚举是无压力的。
#include<cstdio>
#include<cstring>
#include<string>
#include<vector>
#include<algorithm>
using namespace std;
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}, {1, 3, 5, 0, 2, 4}, {0, 3, 1, 4, 2, 5}, {4, 3, 0, 5, 2, 1}, {5, 3, 4, 1, 2, 0},
{3, 4, 5, 0, 1, 2}, {3, 5, 1, 4, 0, 2}, {3, 1, 0, 5, 4, 2}, {3, 0, 4, 1, 5, 2},
};//上面代码生成的状态
const int maxn = 4;
int n, dice[maxn][6], ans;
int r[maxn], color[maxn][6]; // 每个立方体的旋转方式和旋转后各个面的颜色
vector<string> names;
inline int ID(const char* name)
{
string s(name);
int n = names.size();
for (int i = 0; i < n; i++)
if (names[i] == s)
return i;
names.push_back(s);
return n;
}
inline 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]; // 每种颜色出现的次数
memset(cnt, 0, sizeof(cnt));
int maxface = 0;
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();
else
{
for (int i = 0; i < 24; i++)
{
r[d] = i; // 立方体的旋转都试一遍
dfs(d + 1);
}
}
}
int main(void)
{
while (scanf("%d", &n), 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;
}