题目大意 :给出N(1000)个数字(<2^63 -1),从n中取出k个数,使得任意a,b属于k,a,b不形成整除关系。问k最大是多少
算法 :有向图最小路径覆盖数
思路 :因为要求取出一些点,使得他们之间没有整除关系,很容易想到利用整除关系建立一个图,然后看最多有多少个点能不相连,如果把图看作无向图,那么就很难想到做法,至少无向图最大点独立集是不能在1000的规模下运行的。如果a是b的约束,我们建立一条a向b的有向边,最后发现,要求的其实就是最小路径覆盖数。
最小路径覆盖数:在一个有向图中至少放几个人才能走到所有点(延边走,人可以放在任意位置,非官方解释)。
最小路径覆盖 :在一个有向图中,最少用几条路径能把所有的点覆盖(路径不交叉,即每个点只属于一条路)。
当有向图无环时,可以用二分图最大匹配来求解。
最少路径覆盖 = 点数 – 二分图最大匹配
其中二分图中的点是有原图中的点拆点后形成,一定要注意下面两点:
1 :若我们求的是路径不能交叉的最小路径覆盖,则直接二分图就行。
2 :若我们求的是路径能够交叉的最小路径覆盖,组应该先进行floyed,然后再二分图。
假设从在边(a,b)(b,c), (d,b), (b,e)那么Floyed完以后相当于是又从a向c,e另加了边来绕过原来路径中交叉的那个b点,也就是转化成了不能路径交叉的最小路径覆盖。
再回头来看我们这个题。题目要求是求最多的数,按照题目要求,在我们所建立的图里,一条路径代表的是一条约数路,也就是这一条路上我们只能最多拿出一个点,而最小路径覆盖便是所有的这样的边的个数。
选出n个数,任意两个数之间不能出现整除的情况,把能整除的都建边,
要求的就是任意两个之间没有公共边的,就是最大独立集,用最小路径覆盖同样可以。
最少路径覆盖 = 点数 – 二分图最大匹配
#include<stdio.h>
#include<string.h>
int map[1001][1001];
int link[1001];
int mark[1001];
int m;
__int64 a[100000];
int find(int k)
{
int i;
for(i=1;i<=m;i++)
{
if(mark[i]==0&&map[k][i])
{
mark[i]=1;
if(link[i]==0||find(link[i]))
{
link[i]=k;
return 1;
}
}
}
return 0;
}
int main()
{
int i,j,k,h,sum;
scanf("%d",&k);
while(k--)
{
scanf("%d",&m);
memset(map,0,sizeof(map));
memset(link,0,sizeof(link));
for(i=1;i<=m;i++)
{
scanf("%I64d",&a[i]);
for(j=1;j<i;j++)
{
if(a[i]%a[j]==0||a[j]%a[i]==0)
map[j][i]=1;
}
}
sum=0;
for(i=1;i<=m;i++)
{
memset(mark,0,sizeof(mark));
if(find(i))
sum++;
}
printf("%d\n",m-sum);
}
return 0;
}
法二:搜索:
#include <algorithm>
using namespace std;
__int64 a[1005],b[1005];
int t,n,i,j,k,temp;
void BFS(int x)
{
b[x]=0;
for(k=x+1;k<n;k++)
{
if(a[k]%a[x]==0)
{
BFS(k);
break;
}
}
}
int main()
{
scanf("%d",&t);
while(t--)
{
temp=0;
scanf("%d",&n);
for(i=0;i<n;i++)
scanf("%I64d",&a[i]);
sort(a,a+n);
for(i=0;i<n;i++)
b[i]=1;
for(i=0;i<n;i++)
{
if(b[i]==1)
{
temp++;
BFS(i);
}
}
printf("%d\n",temp);
}
return 0;
}