HDU 3335 Divisibility

题目大意:在给出的一堆数当中找出最多的sum个数,使得sum个数两两不能整除

思路:其实还是空袭的意思,派最少的几个人可以把全部街道拿下,在这里就是最少可以把全部的数划分几个集合, 然后再在每个集合里面抽取一个数,即是有几个集合就是最后不能整除的数的个数,其实空袭那里也有最小划分的关系,最小划分就是那几条街道,虽然说路径覆盖数有很多,但是如果问到在街道随意挑出一些人来,问这些人最多有几个不能相互访问,这就不能在多于最小路径覆盖数的集合中找了,因为即使是找了,也会有重复的,即是存在可以相互访问的2个人。而这道题,虽然整除的集合划分也许可以很多,但是最小整除的集合划分才能够保证在每个集合中取出来的数能两两不整除,这个整除的最小划分就是用最小的路径来覆盖全部的点,即是最小路径覆盖数

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef __int64 LL;
int lik[1010];
int n;
LL num[1010];
int head[1010],cnt;
bool flag[1010];
struct node
{
int y,next;
}a[900000];
void ad(int x,int y)
{
a[cnt].next=head[x];//由于点很多,只能用邻接表来建图
a[cnt].y=y;
head[x]=cnt++;
}
bool dfs(int x)
{
for(int i=head[x];i;i=a[i].next)
{
if(flag[a[i].y])continue;
flag[a[i].y]=true;
if(lik[a[i].y]==-1||dfs(lik[a[i].y]))
{
lik[a[i].y]=x;
return true;
}
}
return false;
}
int main()
{
int i,j,T;
scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
memset(head,0,sizeof(head));
cnt=1;
for(i=1;i<=n;i++)
scanf("%I64d",&num[i]);
sort(num+1,num+1+n);
int pre=1;
for(i=2;i<=n;i++)
if(num[pre]!=num[i]) //去掉重复的数
{
num[++pre]=num[i];
}
n=pre;
for(i=1;i<=n;i++)
for(j=1;j<=n;j++)
if(i!=j&&num[i]%num[j]==0) //由整除关系建图
ad(j,i);
memset(lik,-1,sizeof(lik));
int ans=0;
for(i=1;i<=n;i++)
{
memset(flag,0,sizeof(flag));
if(dfs(i))ans++;
}
printf("%d\n",n-ans);
}
}

PS另一解题报告:转载出处:http://blog.sina.com.cn/s/blog_6fa65cf90100rl4g.html

算法 :有向图最小路径覆盖数

思路 :因为要求取出一些点,使得他们之间没有整除关系,很容易想到利用整除关系建立一个图,然后看最多有多少个点能不相连,如果把图看作无向图,那么就很难想到做法,至少无向图最大点独立集是不能在1000的规模下运行的。如果a是b的约束,我们建立一条a向b的有向边,最后发现,要求的其实就是最小路径覆盖数。

最小路径覆盖数:在一个有向图中至少放几个人才能走到所有点(延边走,人可以放在任意位置,非官方解释)。

最小路径覆盖 :在一个有向图中,最少用几条路径能把所有的点覆盖(路径不交叉,即每个点只属于一条路)。

当有向图无环时,可以用二分图最大匹配来求解。

最少路径覆盖 = 点数 – 二分图最大匹配

其中二分图中的点是有原图中的点拆点后形成,一定要注意下面两点:

1 :若我们求的是路径不能交叉的最小路径覆盖,则直接二分图就行。

2 :若我们求的是路径能够交叉的最小路径覆盖,组应该先进行floyed,然后再二分图。

假设从在边(a,b)(b,c), (d,b), (b,e)那么Floyed完以后相当于是又从a向c,e另加了边来绕过原来路径中交叉的那个b点,也就是转化成了不能路径交叉的最小路径覆盖。

再回头来看我们这个题。题目要求是求最多的数,按照题目要求,在我们所建立的图里,一条路径代表的是一条约数路,也就是这一条路上我们只能最多拿出一个点,而最小路径覆盖便是所有的这样的边的个数。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值