您的位置:首页 > 其它

UVA 10601 Cubes (组合数学 + ploya计数)

2015-11-17 17:15 309 查看

题意:

有12根等长火柴,每根火柴的颜色是6种颜色中的一种,输入每根火柴的颜色,求构成的立方体有多少种,旋转后完全相同的立方体视为相同。

思路:

一直以来只理解了ploya的皮毛。。所以看见这种题,可以求出置换种类,循环节个数,长度。。但依然不会计算。

一个立方体的旋转置换有24种,分别是:

静止不动(包含1种置换),循环节有12个,每个长度为1;

以对顶点的连线为轴,旋转120°,240°(包含4 × 2 = 8种置换),两种旋转角度的循环节均有4个,长度为3;

以对边中点的连线为轴,旋转180°(包含6 × 1 = 6种置换),这种置换有两种长度的循环节,分别是轴所在对边构成的两个长度为1的循环节,以及其他边构成的5个长度为2的循环节;

以对面中心的连线为轴,旋转90°,180°,270°(包含3 × 3 = 9种置换),旋转90°与270°的置换有3个长度为4的循环节,旋转180°的置换有6个长度为2的循环节。

知道置换种类与对应的循环节个数与长度后,就可以用组合计数的方式来统计每种置换的方案数,计算方式就是把12根火柴分成每一种置换的各个循环,如果不能分,即12不能整除循环节长度则该置换方案数为0.

处理有不同长度循环节的置换,可以将其拆分开来统计。

(看到有人用6维背包做,惊为天人。。

代码:

#include<bits/stdc++.h>
using namespace std;

typedef unsigned long long lint ;
lint C[13][13] ;
int cnt[7], a[7] ;
void init()
{
C[0][0] = 1;
for (int i = 1; i < 13; i++) {
C[i][0] = 1;
for (int j = 1; j < i + 1; j++) {
C[i][j] = C[i-1][j] + C[i-1][j-1] ; ;
}
}
}

lint count(int k)
{
int n = 0 ;
for (int i = 0; i < 6; i++) {
if (cnt[i] % k == 0) {
cnt[i] /= k ;
n += cnt[i];
}
else return 0 ;
}
lint ret = 1 ;
for (int i = 0; i < 6; i++) {
ret *= C
[cnt[i]] ;
n -= cnt[i] ;
}
return ret ;
}
lint still()
{
memcpy(cnt, a, sizeof a) ;
return count(1) ;
}
lint point()
{
memcpy(cnt, a, sizeof a) ;
return 8 * count(3) ;
}
lint edge()
{
lint ret = 0 ;
for (int i = 0; i < 6; i++)
for (int j = 0; j < 6; j++) {
if (!a[i] || !a[j]) continue ;
memcpy(cnt, a, sizeof a) ;
cnt[i]-- ; cnt[j]-- ;
ret += 6 * count(2) ;
}
return ret ;
}
lint plane()
{
lint ret = 0 ;
memcpy(cnt, a, sizeof a) ;
ret += 3 * count(2) ;
memcpy(cnt, a, sizeof a) ;
ret += 6 * count(4) ;
return ret ;
}
void work()
{
lint ans = 0 ;
ans += still() ;
ans += point() ;
ans += edge() ;
ans += plane() ;
cout << ans / 24 << endl ;
}
int main()
{
init() ;
int t ; cin >> t ;
while (t-- ){
memset(a, 0, sizeof a) ;
for (int i = 0; i < 12; i++) {
int col ; scanf("%d", &col) ;
a[col-1]++ ;
}
work() ;
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  组合数学 ploya