[Advanced] (2017-05-17) 项链

本文探讨如何从给定的一组数字中,选择不重复且可以旋转的M个数字组成项链,计算所有可能的组合数。通过递归和标记法解决复杂重复问题,涉及算法和组合数学应用。

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

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值