查了几个小时,真的查裂开了,所以我必须吐槽一下
给定n个正整数,将它们分组,使得每组中任意两个数互质。至少要分成多少个组?
第一行是一个正整数n。1 <= n <= 10。
第二行是n个不大于10000的正整数。
一个正整数,即最少需要的组数。
6
14 20 33 117 143 175
3
几组问题的解(如下)
由于数据不太大,所以我们可以用搜索的方式解决,先将数字分组,再进行判断,保留最优值ans(组数)
在这里,我运用了动态数组vector,它可以自动开辟数组大小
vetcor<int> v[15] 表示:15个组的动态数组v[]
v[1].push_back(a[1]);
开辟第一组,所以a[1]只能放入第一组
dfs(2,1);
搜索第二个数,当前组数为1
void dfs(int t,int num) 搜索第t个数字的情况,目前开辟的组数
{
if (num>ans) return ;
剪枝,如果组数已经大于最优值ans,就没有再搜的必要了
if (t==n+1)
{
已经将n个数都分好了组,接下来进行判断
if (pd(num)) ans=min(ans,num);
如果符合条件,则将ans更新为当前的组数
return ;
结束,返回上一层(必须写)
}
for (int j=1;j<=num+1;j++)
{
v[j].push_back(a[t]);
将a[t]放入第j组
dfs(t+1,max(num,j));
下一层搜索,如果j=num+1>num,表示为a[t]单独开辟一组
v[j].pop_back();
回溯,将a[t]从j组拿出,重新放进别的组
}
}
判断当前分的数是否满足条件:?
bool pd(int m)
{
判断当前分组是否满足条件
for (int i=1;i<=m;i++)
{
查看第i组
int len=v[i].size();
len记录当前i组有多少个数
for (int j=0;j<len;j++)
{
注意:动态数组将数放入组时,从0号开始放
for (int k=j+1;k<len;k++)
{
判断两两间是否互质(最大公约数gcd==1)
if (gcd(v[i][j],v[i][k])!=1) return false;
不满足,返回false(假)
}
}
}
return true;
满足条件,返回true(真)
}
求两数的最大公约数:
int gcd(int x,int y)
{
int r=x%y;
while (r)
{
x=y;
y=r;
r=x%y;
}
return y;
辗转相除法
}
图形化证明:
在看了许多证明,我认为都没有解释的很清楚,所以我选择用我的方法证明一下
根据上面的代码,我们将其带入,要求的最大公约数x,就是能用x*x的小正方形围成a*b的最大正方形,用当前的短边b*b填充a*b,留下的面积是长边为b,所以在剩下的面积中找出的正方形,一定能围成b*b的正方形,依次类推,求出恰好能填充剩余面积的小正方形,它的边长就是a和b的最大公约数
#include <bits/stdc++.h>
using namespace std;
int n,r,ans=25;
vector<int> v[15];
int a[25];
int gcd(int x,int y)
{
int r=x%y;
while (r)
{
x=y;
y=r;
r=x%y;
}
return y;
}
bool pd(int m)
{
for (int i=1;i<=m;i++)
{
int len=v[i].size();
for (int j=0;j<len;j++)
{
for (int k=j+1;k<len;k++)
{
if (gcd(v[i][j],v[i][k])!=1) return false;
}
}
}
return true;
}
void dfs(int t,int num)
{
if (num>ans) return ;
if (t==n+1)
{
if (pd(num)) ans=min(ans,num);
return ;
}
for (int j=1;j<=num+1;j++)
{
v[j].push_back(a[t]);
dfs(t+1,max(num,j));
v[j].pop_back();
}
}
int main ()
{
int i,j;
cin>>n;
for (i=1;i<=n;i++)
{
cin>>a[i];
}
v[1].push_back(a[1]);
dfs(2,1);
cout<<ans;
return 0;
}
成功Ac,完结撒花~!
如有不解或优化,可在评论区留言