UVA 10601 Cubes(正方体旋转、Polya计数)

使用Burnside引理和Polya计数解决UVA 10601 Cubes问题,分析正方体的24种旋转置换,包括以相对面、对边和对顶点为中心的旋转,计算不同置换下的循环长度,以解决等长火柴棒组成正方体的不同形态数量。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目链接:
UVA 10601 Cubes
题意:
给出12根等长的火柴棒,每根火柴棒的颜色属于 16 中的一种,问能拼成多少种不同的正方体?(考虑旋转变换)
分析:
首先正方体的旋转置换有 24 。下面将每个循环内元素的个数称为循环的长度。注意是棱边的置换循环,而不是面的置换循环

  • 静止。只有一种置换。有 12 个循环,每个循环的长度为 1
  • 以相对面的中心为轴旋转。可以旋转的角度是90,180,270。选择轴有 3 种,每种轴下有3种旋转,所以有 33=9 种置换。
    旋转 90,270 ,都是三个循环,每个循环的长度为 4 .
    旋转180,有6个循环,每个循环的长度为2
  • 以对边中点为轴,只可以旋转 180 ,选择轴有6种,所以有6*1=6种置换。
    在这种旋转下有5个长度为2的循环和2个长度为1的循环
  • 以对顶点为为轴,可以旋转 120240 。选择轴有4种,所以有4*2=8种置换。
    旋转 120,240 都是有4个长度为3的循环。

综上,可以验证置换数为 24 ,可以找一个魔方,模拟旋转看看。找到了在每种置换下的循环个数和每个循环的长度就可以参考UVA 11255 Necklace中处理旋转置换的方法解决。
需要特别关注的是,

180,5221

我们可以先枚举两个长度为1的循环选择的颜色,然后对于5个长度为2的循环就和上面的一样了。
最后别忘了根据 Burnside 需要除以总的置换数 24

#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <algorithm>
#include <climits>
#include <cmath>
#include <ctime>
#include <cassert>
#define IOS ios_base::sync_with_stdio(0); cin.tie(0);
using namespace std;
typedef long long ll;

int T;
int a[10], b[10];
ll C[20][20];

void init()
{
    C[0][0] = 1;
    for(int i = 1; i < 15; ++i) {
        C[i][0] = C[i][i] = 1;
        for(int j = 1; j < i; ++j) {
            C[i][j] = C[i - 1][j] + C[i - 1][j - 1];
        }
    }
}

ll work(int k)
{ //每k条边必须相同,分成12/k组(以对边中点为轴旋转180°是分成5组)
    memcpy (b, a, sizeof(a));
    int sum = 0;
    for (int i = 1; i <= 6; ++i) {
        if(b[i] % k) return 0;
        b[i] /= k;
        sum += b[i];
    }
    ll res = 1;
    for (int i = 1; i <= 6; ++i) {
        res *= C[sum][b[i]];
        sum -= b[i];
    }
    return res;
}

ll solve()
{
    ll res = 0;
    //静止
    res += work(1); 
    //以相对面中心为轴
    res += (ll)3 * 2 * work(4); //旋转90°和270°
    res += (ll)3 * work(2); //旋转180°
    // 以对顶点为轴,可以旋转120°或240°
    res += (ll)4 * 2 * work(3);
    // 以对边种点为轴,只能旋转180°
    for(int i = 1; i <= 6; ++i) {
        for(int j = 1; j <= 6; ++j) {
            if(a[i] == 0 || a[j] == 0) continue;
            a[i]--; a[j]--;  //将a[i]和a[j]设为选择的两条对边的颜色
            res += (ll)6 * work(2); //剩下的是5个循环长度为2的循环,6代表对边选择情况
            a[i]++; a[j]++;
        }
    } 
    return res;
}

int main()
{
    init();
    scanf("%d", &T);
    while (T--) {
        memset (a, 0, sizeof(a));
        for (int i = 0; i < 12; ++i) {
            int tmp;
            scanf ("%d", &tmp);
            a[tmp]++;
        }
        printf("%lld\n", solve() / 24); // 最后还要除以总的置换数:24
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值