题意
给定 n 个正整数,将它们分组,使得每组中任意两个数互质。
至少要分成多少个组?
分析
判断互质 -> GCD b ? gcd(b,a%b) : a;
我们要执行的操作有两种
- 把某个数加到最后一组中
- 新开一个组
代码
//
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 10;
int n;//数组大小
int p[N];//存数组
int group[N][N]; //存放每个组
bool st[N];//判重数组
int ans = N;
int gcd(int a, int b) // 判断是否互质
{
return b ? gcd(b,a%b) : a;
}
// check的逻辑 判断当前组内的数 是否和该数都互质, 即新的数 是能到组内,还是新建一个组
bool check(int group[], int gc, int i)
{
for(int j = 0; j < gc; j ++)// 枚举组内每个数
if(gcd(p[group[j]],p[i]) > 1) // p[group[j]] ?
return false;
return true;
}
//一组一组地进行组合型枚举,通过加入start参数避免了组内的冗余(排列变组合),
//但是组与组之间仍然有冗余,
//比如考虑第一组时,放1和3,考虑第二组时放2,等到回溯以后没有必要再考虑第一组放2的情况,
//这样就可以把组与组之间的顺序给排除掉了。
//g为当前的最后一组的组的序号, gc为当前组内搜索到的数的下标 && 当前组内有多少个数;
//tc为当前搜索过的数的数量, start为当前组开始搜索的数的下标;
void dfs(int g, int gc, int tc, int start)
{
if(g >= ans) return; //如果有比当前最优解所需要的数组更多的解法,说明此解不是最优解
if(tc == n) ans = g; // 如果当前已经搜索完了,则更新最优解;
bool flag = true; //flag 判断是否能开新的一组
for(int i = start; i < n; i ++) //枚举每个数
if(!st[i] && check(group[g],gc,i))//当前的数还未被放入组内 并且 此数与当前组内的数都互质
{
st[i] = true; // 当前数已放入组内, 打标记
group[g][gc] = i; //讲此数放进该组
dfs(g, gc + 1, tc + 1, i + 1);
//继续搜索该组, 组内数的数量 gc + 1, 总数 tc + 1, 搜索的数的下标i + 1;
st[i] = false;//回溯
flag = false;//如果能放进最后一组,则不用开新组
if (gc == 0) return;//剪枝,减低冗余 逻辑?
}
if(flag) dfs(g + 1, 0, tc, 0);
//如果无法放入最后一组, 则新开一组
//当前最后一组的组序号 g + 1, 组内搜索的数的序号 gc = 0;
//搜索到的数 tc, 当前组内开始搜索的数的序号 start = 0;
}
int main()
{
cin >> n;
for(int i = 0; i < n;i ++) cin >> p[i];
dfs(1,0,0,0); //
// 第一个组 g = 1, 组内没有数 gc = 0;
// 还没有搜索到点tc =0, 当前组内还未开始遍历
cout << ans << endl;
return 0;
}