【ACWing】1118. 分成互质组

题目地址:

https://www.acwing.com/problem/content/1120/

给定 n n n个正整数,将它们分组,使得每组中任意两个数互质。至少要分成多少个组?

输入格式:
第一行是一个正整数 n n n。第二行是 n n n个不大于 10000 10000 10000的正整数。

输出格式:
一个正整数,即最少需要的组数。

数据范围:
1 ≤ n ≤ 10 1≤n≤10 1n10

我们可以依次枚举第 i i i个组可以放哪些数,每次枚举完第 i i i个组放哪些数之后,再枚举第 i + 1 i+1 i+1个组可以放哪些数,以此类推。在枚举第 i i i个组放哪些数的时候,因为这个组里的数是按照何种顺序放进去的是没有区别的,所以我们可以人为地按次序枚举这 n n n个正整数,即按下标从小到大枚举这些数。当枚举第 i i i组放哪个数的时候,如果当前备选的数里都不能放了,那显然要开个新组,即第 i + 1 i+1 i+1组,然后继续从第 i + 1 i+1 i+1组开始枚举放哪些数;如果能放,那么此时仍然有两种选择,要么放进第 i i i组,要么新开一组然后放进第 i + 1 i+1 i+1之后的某个组。我们可以证明,对于第二种选择的任意合法的解,都有一个不会更差的解与之对应:如果此时是将这个数放在了新开的第 i + 1 i+1 i+1组后面的某个组,那么形成的新的解中,可以将这个数挪到第 i i i组里面去,这时仍然是个合法的解,并且组数不会变多(而且还有可能变得更少)。所以这就造成了,最优解一定在第一种方案里,所以我们不必枚举第二种方案,只需“能放就放”即可(严格证明可以用构造法,如果某个解是第二种方案得到的,即新开组得到的,那么可以先将每个组里的数按下标从小到大排序,接着再将这些组按照组内第一个元素的下标从小到大排序,然后进行挪动操作,先将能加到第 1 1 1个组里的数都挪到第 1 1 1个组里,并添加到最后面,也就是说只挪那些下标比第 1 1 1个组里最后一个数下标更大的数,然后再看第 2 2 2个组,将能加到第 2 2 2个组里的数都挪到第 2 2 2个组里,并添加到后面,以此类推。该过程一定可以在有限步内停止。停止所得的解一定是个合法解,并且不会更差,并且这个解是算法能够搜到的那个解。所以就证明了算法是可以搜到最优解的)。代码如下:

#include <iostream>
using namespace std;

const int N = 11;
int n;
int a[N];
int g[N][N];
int res = N;
bool st[N];

int gcd(int a, int b) {
    
    
    return b ? gcd(b, a % b) : a;
}

// 看一下第u组是否能加入a[idx]这个数
bool check(int u, int gc, int idx) {
    
    
    for (int i = 0; i < gc; i++)
        if (gcd(a[g[u][i]], a[idx]) > 1) return false;

    return true;
}

// u是正在枚举第几组,gc是正在枚举的这一组里已经有了多少个数,
// tc是已经枚举了多少个数了,start表示从a[start]开始枚举
void dfs(int u, int gc, int tc, int start) {
    
    
	// 枚举到了不优于res的答案,则直接返回,不用继续枚举
    if (u >= res) return;
	
	// 枚举到一个更优解,则记录之并返回
    if (tc == n) {
    
    
        res = u;
        return;
    }

    bool added = false;
    // 枚举第u组还能添加哪个数
    for (int i = start; i < n; i++) {
    
    
    	// 如果a[i]没被选过,并且确实能加进第u组里去,则枚举这种情况
        if (!st[i] && check(u, gc, i)) {
    
    
            st[i] = true;
            g[u][gc] = i;

            dfs(u, gc + 1, tc + 1, i + 1);

            st[i] = false;
            added = true;
        }
    }
	
	// 只要当前第u组还能加进一个数,那么就不枚举不加这个数的方案
    if (!added) dfs(u + 1, 0, tc, 0);
}

int main() {
    
    
    cin >> n;
    for (int i = 0; i < n; i++) cin >> a[i];

    dfs(1, 0, 0, 0);

    cout << res << endl;

    return 0;
}

时间复杂度指数级,空间 O ( n ) O(n) O(n)

猜你喜欢

转载自blog.csdn.net/qq_46105170/article/details/114817805