DFS:分成互质组

这篇博客介绍了两种不同的C++算法实现,目标是在一组整数中找到最少的组数,使得每组内的整数两两互质。第一种算法基于深度优先搜索(DFS),另一种则采用类似贪心的策略。代码对比显示,贪心策略的效率更高,运行速度可以达到DFS的20倍。博客通过具体的代码示例解释了这两种方法的工作原理和优化细节。
摘要由CSDN通过智能技术生成

原题链接:https://www.acwing.com/problem/content/1120/
在这里插入图片描述

扩展选择:已存在的组+每次尝试开辟一个新的组

参考的代码比y总快20倍:

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

const int N = 15;
vector<int> g[N]; // 开的存放互质的数的数组
int nums[N]; // 读入数据
int ans = N; // 初始化 大家各自一组
int n;

// 返回a和b的 最大公约数
int gcd(int a, int b)
{
    return b ? gcd(b, a % b) : a;
}

// 判断x是否与vec中的每个数互质
bool check(vector<int>& vec, int x)
{
    for (int i = 0; i < vec.size(); i++)
        if (gcd(vec[i], x) > 1) return false;
    return true;
}

// u是当前处理到序列的下标,used是目前使用到的组数
void dfs(int u, int used)
{
    // 剪枝:如果当前使用的组已经>=目前获得的最优解,停止当前分支的搜索
    if (used + 1 >= ans) return;
    
    // 如果处理完了所有的数
    if (u >= n)
    {
        // 在每个到达最终目的地的分支中取一个最小值
        ans = min(ans, used + 1);
        return;
    }
    
    // 先在已经使用的组里面找,看看能不能插进去
    for (int i = 0; i <= used; i++)
    {   
        // 如果可以放进去,直接处理u+1的数
        if (check(g[i], nums[u]))
        {
            g[i].push_back(nums[u]);
            dfs(u + 1, used);
            // 回溯
            g[i].pop_back();
        }
    }

    // 再开辟一个新的数组,尝试一下。
    // 剪枝:因为n个数最多只放进n个组,所以只要总组数<=n,允许尝试放进新的组
    // > n 的话,直接就不让放了,就相当于退出
    if (used + 1 <= n)
    {
            // 尝试开辟新的组
            g[used + 1].push_back(nums[u]);
            dfs(u + 1, used + 1);
            g[used + 1].pop_back();
    }
}

int main()
{
    scanf("%d", &n);

    for (int i = 0; i < n; i++) scanf("%d", &nums[i]);

    // 初始处理nums[0], 初始放入g[0]
    dfs(0, 0);

    printf("%d\n", ans);

    return 0;
}

但是Y总说:
依次枚举每个组,优先往互质的组里放,如果放不下,再去开新的组。
每次扩展的时候,枚举当前所有的组,看一下是不是和他们互质,即能不能放即可。

像一个贪心的思想。可能是因为DFS穿的参数太多。时间反而长。
y总的代码:

#include <iostream>

using namespace std;

const int N = 15;

int n;
int p[N]; // 存放数
int group[N][N]; // 存放每个组
bool st[N]; // 标记每个数是否被放进了组内
int ans; // 当前所存在的最优解

int gcd(int a, int b) { // gcd求最大公约数
    return b ? gcd(b, a % b) : a;
}

bool check(int g[], int gc, int num) { // 判断当前组中的数是否和该数都互质(即该数能否放进该组)
    for (int i = 0; i < gc; ++ i) // 枚举此组组内每个数
        if (gcd(g[i], p[num]) > 1 ) // 只要组内有数和该数不互质了就 return false
            return false;
    return true; // 否则 return true
}

void dfs(int g, int gc, int tc, int start) { 
        // g为当前的最后一组的组的序号, gc为当前组内搜索到的数的序号;
        // tc为当前搜索过的数的数量, start为当前组开始搜索的数的序号

    if (g >= ans) return ; // 如果有比当前最优解所需的组数更多的解法说明此解不为最优解-->直接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] = p[i]; // 将该数放进该组
            dfs(g, gc + 1, tc + 1, i + 1); 
                // 继续搜索该组,组内数的数量gc + 1,总的数的数量tc + 1,搜索的数的序号i + 1
            st[i] = false; // 恢复
            flag = false; // 如果能放进当前最后一组,则不用新开一组,故flag标记为false
        }

    if (flag) dfs(g + 1, 0, tc, 0); 
        // 如果无法放进最后一组,则新开一组来搜索
            // 当前最后一组的组的序号g + 1, 组内搜索的数的序号gc为0;
            // 搜索到的数tc未变, 当前组内开始搜索的数的序号start为0
    /* 此时的dfs操作其实就相当于开了一个组开始搜索的操作,还没有放数进来 */
}

int main() {
    cin >> n;
    ans = n; // 还未搜索时,假定最优解为最坏的情况每个数都分一组

    for (int i = 0; i < n; ++ i) scanf("%d", p + i);

    dfs(1, 0, 0, 0); 
    // 从第一个组g = 1, 组内没有数gc = 0;
    // 还没搜索到点tc = 0, 当前组还未开始遍历数start = 0的初始状态开始搜索

    cout << ans << endl; // 输出搜索完后的最优解

    return 0; // 结束快乐~
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值