您的位置:首页 > 其它

例题1.8 彩色立方体 Colored Cubes UVALive - 3401 暴力打表+暴力搜索+贪心

2017-08-11 12:36 459 查看
传送门

题目大意:有n个带颜色的立方体,每个面都涂有不同的颜色,要求图改尽量少的面,使得所有立方体完全相同,立方体可以旋转。

解题思路:如果立方体每个面都不一样,那么立方体通过旋转一共有4×6=24中状态。我们给每个面标号0,1,2,3,4,5  那么旋转可以生成24中序列。这个序列手写很麻烦,可以用程序生成。然后将这二十四个状态当作常量表,直接使用即可。

 第一个立方体作为参照,然后就是枚举每个立方体的状态,用递归实现即可,递归到最后一个立方体时,计算每个面需要涂改的面,得出每种状态需要涂改的面。找出所有情况的最小值即可。

生成常量表的程序代码:

#include <iostream>
#include <cstdio>
#include <cstring>

using namespace std;

int main()
{
int A[] = {0,1,2,3,4,5};
for(int i=0; i<4;i++) //上前下后四个面分别当顶面
{
for(int j = 0; j<4; j++)
{
int m = A[0];
A[0] = A[1];
A[1] = A[5];
A[5] = A[4];
A[4] = m;
printf("{%d, %d, %d, %d, %d, %d},", A[0], A[1], A[2], A[3], A[4], A[5]);
}
int x = A[2];
A[2] = A[0];
A[0] = A[3];
A[3] = A[5];
A[5] = x;
}
int x = A[2]; //右面当顶面
A[2] = A[1];
A[1] = A[3];
A[3] = A[4];
A[4] = x;
for(int j = 0; j<4; j++)
{
int m = A[0];
A[0] = A[1];
A[1] = A[5];
A[5] = A[4];
A[4] = m;
printf("{%d, %d, %d, %d, %d, %d},", A[0], A[1], A[2], A[3], A[4], A[5]);
}
x = A[2];//左面当顶面
A[2] = A[1];
A[1] = A[3];
A[3] = A[4];
A[4] = x;
x = A[2];
A[2] = A[1];
A[1] = A[3];
A[3] = A[4];
A[4] = x;
for(int j = 0; j<4; j++)
{
int m = A[0];
A[0] = A[1];
A[1] = A[5];
A[5] = A[4];
A[4] = m;
printf("{%d, %d, %d, %d, %d, %d},", A[0], A[1], A[2], A[3], A[4], A[5]);
}
return 0;
}


AC代码:

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <queue>
#include <map>
#include <stack>
#include <vector>
#include <string>

#define mem(s) memset(s, 0, sizeof(s))

using namespace std;

int n, pos[25], dice[4][6], ans = 1 << 31 -1;
int Vist[24][6] = { {1, 5, 2, 3, 0, 4},{5, 4, 2, 3, 1, 0},{4, 0, 2, 3, 5, 1},{0, 1, 2, 3, 4, 5},{1, 2, 0, 5, 3, 4},{2, 4, 0, 5, 1, 3},{4, 3, 0, 5, 2, 1},{3, 1, 0, 5, 4, 2},{1, 0, 3, 2, 5, 4},{0, 4, 3, 2, 1, 5},{4, 5, 3, 2, 0, 1},{5, 1, 3, 2, 4, 0},{1, 3, 5, 0, 2, 4},{3, 4, 5, 0, 1, 2},{4, 2, 5, 0, 3, 1},{2, 1, 5, 0, 4, 3},{3, 5, 1, 4, 0, 2},{5, 2, 1, 4, 3, 0},{2, 0, 1, 4, 5, 3},{0, 3, 1, 4, 2, 5},{2, 5, 4, 1, 0, 3},{5, 3, 4, 1, 2, 0},{3, 0, 4, 1, 5, 2},{0, 2, 4, 1, 3, 5}
};

int Color[25];

vector<string> names;
int ID(const char * name) //给每种颜色一个ID
{
string s(name); //把name赋值给s, 等同于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;
}

void turn(int x)
{
if(x == n) //找到最后一个立方体了。
{
int sum = 0;
for(int i=0; i<6; i++)
{
int max_color = 0;
mem(Color);
for(int j = 0; j<n; j++)
{
//这一句可能比较难想, 详细解释一下:Color保存的时所有颜色在这个面(所有立方体)出现的次数
//dice保存的是原来立方体的每个面的颜色
//Vist保存的是旋转后 0,1,2,3,4,5,面对应原来哪个面的颜色
//pos保存搜索的哪个姿态。
//综合就是pos姿态下找到当前颜色对应的原来的哪个面,然后得出哪个颜色,让这个颜色+1. 实在想不通可以看下面蓝书的代码,这一步上我省了一个步骤,那个要好理解,道理一样
max_color = max(max_color, ++Color[ dice[j][ Vist[pos[j] ][i] ] ]);
}
sum += n - max_color;
}
ans = min(ans, sum);
}
else for(int i=0; i<24; i++)
{
pos[x] = i;
turn(x+1);//搜索下一个立方体
}
}

int main()
{
char name[30];
while(scanf("%d", &n) && n)
{
ans = 1 << 31 -1;
names.clear();
for(int i=0; i<n; i++)
for(int j =0; j<6; j++)
{
scanf("%s", name);
dice[i][j] = ID(name); //记录每个立方体每个面的颜色
}
turn(0);
printf("%d\n", ans);
}
return 0;
}


蓝书代码:

打表:

// LA3401/UVa1352 Colored Cubes: table maker
// Rujia Liu

#include<cstdio>
#include<cstring>

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 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;
}

主程序:

// LA3401/UVa1352 Colored Cubes
// Rujia Liu

// generated by la3401_make.cpp, then removed extra spaces
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},
};

#include<cstdio>
#include<cstring>
#include<string>
#include<vector>
#include<algorithm>
using namespace std;

const int maxn = 4;
int n, dice[maxn][6], ans;

vector<string> names;
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;
}

int r[maxn], color[maxn][6]; // 每个立方体的旋转方式和旋转后各个面的颜色

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() {
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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: