UVAL colored cubes 打表 暴力枚举

6 篇文章 0 订阅
2 篇文章 0 订阅

题意:有n个带颜色的立方体,每个面涂有一种颜色。要求重新涂尽量少的面,使得所有立方体完全相同。两个立方体完全相同的含义是:存在一种旋转方式,使得两个立方体对应面的颜色完全相同。

思路:因为立方体的个数比较少,我们可以暴力解决。

           首先,我们要求立方体的旋转方式。可以计算得到,有24种旋转方式。但是,如果手动求出这些方式,会很麻烦的,也很容易犯错。

           每种选择对对应一种全排列P。其中,p[i]表示编号i所在的位置(1表示正面,2表示右面,3表示上面,4表示下面,5表示左面,6表示后面)。标准状态为1,2,3,4,5,6;

对于标准状态向左旋转,对应的排列为:5,1,3,4,6,2,向上旋转为:3,2,6,1,5,4。

对于这两个操作,我们可以把它们看做基本操作,其他状态可以看做是这些基本操作组合得到的:

         1.1在顶面的状态:向上翻一次,再左转0-3次。

         2.2在顶面的状态,向左转一次,在左转0-3次。

         3.3在顶面的状态: 直接左转0-3次。

         4.4在顶面的状态:向上翻转2次,再向左转0-3次。

         5.5在顶面的姿态:向左转3次,向上翻一次,再向左转0-3次、

         6.6在顶面的状态,向左转2次,再向上翻一次,再向左转0-3次。

生成24中旋转的代码如下:

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


 这样,我们就得到了24种旋转方式。

但是如何再暴力的做呢?如果只是简单的枚举每个面的颜色,然旋转,判断是否相同,复杂度为24 ^ 6,不可行。

如果以第一个正方体为标准,枚举其他三个正方体旋转的状态。然后,对于每个面,选出出现次数最多的颜色,将其他不同的颜色重涂为该颜色。这样,复杂度为24^3.

代码如下:

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


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值