题目

信息学奥赛一本通(C++版)在线评测系统 给定n个正整数,将它们分组,使得每组中任意两个数互质。至少要分成多少个组?

输入

第一行是一个正整数n。1 ≤ n ≤ 10。

第二行是n个不大于10000的正整数。

输出

一个正整数,即最少需要的组数。

输入样例

6
14 20 33 117 143 175

输出样例

3

思路

如果按之前区间分组的思路, 当此点无法加入到之前的组中, 则自己开一个组。

问题在于怎么维护这几个组。可以使用vector二维数组。

在DFS时, 当前点有n+1种选择, 加入之前的n组(如果可以加的话), 自己新创一个组。

判断是否互质可以定义一个check函数。

若把每个数当一个节点孩子, 不互质的两个点连一条边表示互相仇恨, 那么就是求该图中互相不仇恨的点分组方案数。最大团问题。

这里数据范围很小, 故不需要考虑那么多。依然是采用决策树的方式来思考。先考虑第一个组, 再考虑第二个组。 考虑第一个组时会一直把能放到第一个组的所有点都放进去, 到第二个组时也是一样。这样在新来点的时候就不需要一次考虑之前所有组, 只需要考虑当前组即可。

一般出成爆搜都是NP完全问题, 否则就会是贪心DP问题。

代码

const int N = 11;
int group[N][N];
int n;
int res = N - 1;
int a[N];
bool st[N];
 
int gcd(int a, int b)
{
    return b ? gcd(b, a % b) : a;
}
 
bool check(int u, int gc, int start)
{
    for (int i = 0; i < gc; i++)
        if (gcd(a[group[u][i]], a[start]) > 1)
            return false;
    return true;
}
 
void dfs(int u, int gc, int tc, int start)
{
    if (u >= res)
        return;
    if (tc == n)
        res = u;
    bool flag = true;
    for (int i = start; i < n; i++)
    {
        if (!st[i] && check(u, gc, i))
        {
            st[i] = true;
            group[u][gc] = i;
            dfs(u, gc + 1, tc + 1, i + 1);
            st[i] = false;
            flag = false;
        }
    }
    if (flag)
        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;
}