给出N(N<=10)个数字,可能重复,从中选出M(3<=M<=6)个组成一串项链,一共有多少种组法
(注意组成项链后,可以旋转,只算一种,如123,231,312,算一种)
输入:
用例数T
N, M
N个数
输出:组法种数
input:
7
3 3
1 2 3
4 4
1 2 2 3
5 5
5 6 5 7 8
6 4
3 3 3 4 4 4
7 6
2 3 1 5 4 6 8
4 2
1 1 1 1
4 2
1 1 2 2
output:
#1 2
#2 3
#3 12
#4 4
#5 840
#6 1
#7 3
//答案:
// 最终,也只能是进行标记处理。每得到一种结果,就转M次,所有变种全部做标记
// 题中给出M最大为6,可能也是为了标记数组大小考虑
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int T, N, M, number[10], circle[6], answer;
bool selectFlag[10], statFlag[1000000];
int getFlagValue(){
int result = 0;
int times = 1;
for (int i = 0; i < M; i++){
result += circle[i] * times;
times *= 10;
}
return result;
}
void markAll(){
// statFlag[getFlagValue()] = true;
// Shift M次,正好最后变回原形,所以就没必要单独mark
for (int i = 0; i < M; i++){
int tmp = circle[0];
for (int j = 0; j <= M - 2; j++){
circle[j] = circle[j + 1];
}
circle[M - 1] = tmp;
statFlag[getFlagValue()] = true;
}
}
void dfs(int steps){
if (steps == M){
if (!statFlag[getFlagValue()]){
answer++;
markAll();
}
return;
}
// 一个可选的优化:直接使得当前step不会出现重复数字
bool flagCurr[10] = { false };
for (int i = 0; i < N; i++){
if (!selectFlag[i] && !flagCurr[number[i]]){
selectFlag[i] = true;
flagCurr[number[i]] = true;
circle[steps] = number[i];
dfs(steps + 1);
selectFlag[i] = false;
// 注意这里不要把flagCurr[number[i]]还原成false
}
}
}
int main(int argc, char** argv) {
freopen("sample_input.txt", "r", stdin);
setbuf(stdout, NULL);
scanf("%d", &T);
for (int test_case = 1; test_case <= T; ++test_case){
scanf("%d %d", &N, &M);
for (int i = 0; i < N; i++){
scanf("%d", &number[i]);
selectFlag[i] = false;
}
for (int i = 0; i < 1000000; i++){
statFlag[i] = false;
}
answer = 0;
dfs(0);
printf("#%d %d\n", test_case, answer);
}
return 0;
}