分成互质组
分成互质组
给定 个正整数,将它们分组,使得每组中任意两个数互质。
至少要分成多少个组?
输入格式
第一行是一个正整数 。
第二行是 个不大于 的正整数。
输出格式
一个正整数,即最少需要的组数。
数据范围
输入样例:
6 14 20 33 117 143 175
输出样例:
3
解题思路
记录一下这道题,对搜索顺序不是很理解,以及搜索时如何消除冗余(排列变组合)。
首先给出比较容易想到的搜索顺序,就是从前往后枚举每一个数,对于当前数,枚举一下这个数可以放到已有的哪个组中,如果某个组可以放入这个数,那么就把这个数放入这个组中,然后接着枚举下一个数。当然这个数还可以放入到一个新的空组中,这种情况也要枚举。
AC代码如下:
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 const int N = 15; 5 6 int n; 7 int a[N]; 8 vector<int> g[N]; 9 int ans = N; 10 11 int gcd(int a, int b) { 12 return b ? gcd(b, a % b) : a; 13 } 14 15 bool check(int x, int cnt) { 16 for (auto &it : g[cnt]) { 17 if (gcd(x, it) != 1) return false; 18 } 19 return true; 20 } 21 22 void dfs(int u, int cnt) { 23 if (cnt >= ans) return; 24 if (u == n) { 25 ans = cnt; 26 return; 27 } 28 29 // 枚举放入哪个组 30 for (int i = 0; i < cnt; i++) { 31 if (check(a[u], i)) { 32 g[i].push_back(a[u]); 33 dfs(u + 1, cnt); 34 g[i].pop_back(); 35 } 36 } 37 38 // 放入新的组的情况 39 g[cnt].push_back(a[u]); 40 dfs(u + 1, cnt + 1); 41 g[cnt].pop_back(); 42 } 43 44 int main() { 45 cin >> n; 46 for (int i = 0; i < n; i++) { 47 cin >> a[i]; 48 } 49 50 dfs(0, 0); 51 cout << ans; 52 53 return 0; 54 }
第二种搜索顺序是枚举每一个组可以放哪些数。比如对于第一组,先枚举所有的数,看哪些数可以放入第一组中。然后是第二组,从剩余没被选的数中看一下哪些数可以放入第二组中,以此类推直到所有的数都已经被选择。说实话对这种搜索顺序总感觉不是很理解,有点怪怪的。
然后是判断搜索的过程中是否有冗余。当考虑哪些数要放入这组的时候其实是个组合问题,比如说放入(这个是数的下标,下同),或者是,或者是,最终的结果是一样的,因为组内顺序不会影响结果。而要实现这种组合的枚举方式只要人为定义一种顺序就好了,即按照下标从小到大往组内加数,实现的方式是搜索时传入一个参数,表示从这个下标开始添加数。
刚才是组内的冗余,然后是每个组之间的冗余。可以知道当每一个组都已经添加完数了,那么各个组间的顺序是怎么样都不会影响最终结果,因为这是个组合问题。比如说当前有两个组,如果变换一下顺序有,可以发现各个组间的顺序是不影响结果的。为了避免搜索排列的情况,在某个组搜索完添加哪些数时,可以判断一下这个组是不是为空,如果是空的那么就直接回溯就好了。这是因为这个组是一个新组,必然会放入还没被选的数中第一个数,因此当搜索完所有的情况后,如果继续往这个组中添加没被选的下一个数,就会产生排列的情况,后面某个组会放入没被选的数中第一个数,就会有冗余。
AC代码如下:
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 const int N = 15; 5 6 int n; 7 int a[N]; 8 vector<int> g[N]; 9 int ans = N; 10 11 int gcd(int a, int b) { 12 return b ? gcd(b, a % b) : a; 13 } 14 15 bool check(int x, int cnt) { 16 for (auto &it : g[cnt]) { 17 if (gcd(x, it) != 1) return false; 18 } 19 return true; 20 } 21 22 void dfs(int u, int cnt, int st) { 23 if (cnt + 1 >= ans) return; 24 if (st == (1 << n) - 1) { // 所有的数都被选了 25 ans = cnt + 1; 26 return; 27 } 28 29 // 当前是第cnt个组 30 bool flag = false; // 用来标记这个组在本次枚举中有没有加入数 31 for (int i = u; i < n; i++) { // 从u开始枚举,避免组内冗余 32 if (st >> i & 1) continue; // 第i个数被选了 33 if (check(a[i], cnt)) { 34 g[cnt].push_back(a[i]); 35 dfs(i + 1, cnt, st | 1 << i); 36 g[cnt].pop_back(); 37 flag = true; 38 if (g[cnt].empty()) return; // 这个组是新组,已经枚举完组合的情况了,回溯,避免组间冗余 39 } 40 } 41 if (!flag) dfs(0, cnt + 1, st); // 本次枚举已经没有可以加的数了,开新组 42 } 43 44 int main() { 45 cin >> n; 46 for (int i = 0; i < n; i++) { 47 cin >> a[i]; 48 } 49 50 dfs(0, 0, 0); 51 cout << ans; 52 53 return 0; 54 }
参考资料
AcWing 1118. 分成互质组(算法提高课):https://www.acwing.com/video/477/
本文来自博客园,作者:onlyblues,转载请注明原文链接:https://www.cnblogs.com/onlyblues/p/16717146.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效