A,2,3,4,5,6,7,8,9 共9张纸牌排成一个正三角形(A按1计算)。要求每个边的和相等。
下图就是一种排法(如有对齐问题,参看p1.png)。
A
9 6
4 8
3 7 5 2
这样的排法可能会有很多。
如果考虑旋转、镜像后相同的算同一种,一共有多少种不同的排法呢?
请你计算并提交该数字。 答案:144
注意:需要提交的是一个整数,不要提交任何多余内容。
解析:先说一下题目中的旋转的意思:正三角形有三个角,所以一个数字可以在三个角各出现一次,这就相当于旋转。
题目中滤镜的意思:在生活中你照镜子的时候会发现,当你抬起左手时,你会看到镜子中的你会抬起右手。在本题中滤镜前后包括一个正三角形和该正三角形左右位置对称交换后的正三角形。
对于这个题我有两种解法。
解法1:全排列法,套用全排列的解题模板模板加上适合的判断条件就可以做出来,先定义一个int类型的(c[9]),正三角形中的9个数字的位置可以固定下数组的下标,如下所示:
A 0
9 6 对应下标 1 8
4 8 2 7
3 7 5 2 3 4 5 6
三条边可表示为: 如果三边相等,记录下个数(包含旋转(3种)和滤镜(2种)的情况),最后让总个数/3/2即可
int A = c[0]+c[1]+c[2]+c[3];
int B = c[3]+c[4]+c[5]+c[6];
int C = c[6]+c[7]+c[8]+c[0];
代码如下:(大约10ms左右)
public class 纸牌三角形全排列法
{
private static int cnt = 0;
public static void Swap(int[] c,int i,int j)
{
int tmp = c[i];
c[i] = c[j];
c[j] = tmp;
}
public static void AllPermutation(int[] c,int start) //全排列算法
{
if(start==c.length-1)
{
int A = c[0]+c[1]+c[2]+c[3];
int B = c[3]+c[4]+c[5]+c[6];
int C = c[6]+c[7]+c[8]+c[0];
if(A==B&&B==C) //三边和相等
{
cnt++; //cnt的个数包含旋转(一个数字可分别出现在一个角一次)、滤镜(数字对称交换)的情况
}
}
else
{
for(int i=start,t=c.length;i<t;++i)
{
Swap(c,i,start); //交换
AllPermutation(c,start+1);
Swap(c,start,i); //交换
}
}
}
public static void main(String[] args)
{
long start = System.currentTimeMillis(); //与本题无关,计算耗时
int[] c = {1,2,3,4,5,6,7,8,9};
AllPermutation(c,0); //全排列算法
System.out.println(cnt/3/2); 去除旋转和滤镜,一个数字可以出现3次,出现滤镜2次
long last = System.currentTimeMillis() -start; //与本题无关,计算耗时
System.out.println("耗时:"+last+"ms"); //与本题无关,计算耗时
}
}
运行结果:
144
耗时:6ms
解法2:回溯法,其实思想和全排列的思想差不多的,不同的是代码的模板不同、数组下标的位置不同,如下所示
A 1
9 6 对应下标 2 9
4 8 3 8
3 7 5 2 4 5 6 7
代码如下:(大约100ms左右)
public class 纸牌三角形回溯法
{
public static void main(String[] args)
{
long start = System.currentTimeMillis(); //与本题无关,计算耗时
int n = 9; //总国有9个数
int[] a = new int[n+2];
int i = 1;
a[i] = 1;
int g;
int cnt = 0;
while(true)
{
g = 1;
for(int k=i-1;k>=1;--k)
{
if(a[i]==a[k]) //不允许有重复的数字出现
{
g = 0;
break;
}
}
if(g!=0&&i==n) //凑齐9个不一样的数字
{
int A = a[1]+a[2]+a[3]+a[4];
int B = a[4]+a[5]+a[6]+a[7];
int C = a[7]+a[8]+a[9]+a[1];
if(A==B&&B==C) //三边和相等
{
cnt++; //cnt的个数包含旋转(一个数字可分别出现在一个角一次)、滤镜(数字对称交换)的情况
}
}
if(i<n&&g!=0)
{
++i;
a[i] = 1;
continue;
}
while(a[i]==n&&i>1)
{
--i;
}
if(a[i]==n&&i==1)
{
break;
}
else
{
a[i] = a[i] + 1;
}
}
System.out.println(cnt/3/2); //去除旋转和滤镜,一个数字可以出现3次,出现滤镜2次
long last = System.currentTimeMillis() - start; //与本题无关,计算耗时
System.out.println("耗时:"+last+"ms"); //与本题无关,计算耗时
}
}
运行结果:
144
耗时:90ms
练习题:纸牌三角形
A,2,3,4,5,6,7,8,9 共9张纸牌排成一个正三角形(A按1计算)。要求每个边的和相等。
下面就是一种排法
A
9 6
4 8
3 7 5 2
这样的排法可能会有很多。
如果考虑旋转、镜像后相同的算同一种,一共有多少种不同的排法呢?
以下程序实现了这一功能,请你补全以下空白处内容:
#include <bits/stdc++.h>
#define ll long long
using namespace std;
int main()
{
int a[9] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
int res = 0;
do
{
int x1 = a[0] + a[1] + a[2] + a[3];
int x2 = a[3] + a[4] + a[5] + a[6];
int x3 = a[6] + a[7] + a[8] + a[0];
__________________;
} while (next_permutation(a, a + 9));
cout << res / 3 / 2 << endl;
return 0;
}
提示:
1、正三角形有三个角,所以一个数字可以在三个角各出现一次,这就相当于旋转。
2、在生活中你照镜子的时候会发现,当你抬起左手时,你会看到镜子中的你会抬起右手。在本题中滤镜前后包括一个正三角形和该正三角形左右位置对称交换后的正三角形
A | if (x1 == x2 && x2 == x3) { res++; } |
B | if (x1 == x2 || x1 == x3) { res++; } |
C | if (x1 == x2) { res++; } |
D | if (x1 == x2 || x2 == x3) { res++; } |
答案:
if (x1 == x2 && x2 == x3)
{
res++;
}