分析:
dlx重复覆盖的巧用,重复覆盖的原理恰好符合本题的筛选方式,即选择一个数后,该数的倍数或约数可以保证在之后的搜索中不会被选择
于是修改一下启发函数,求解最大的重复覆盖即可。
其实不一定不被选择,只是选择以后,要么达不成目标,要不达到目标,也不如不选择更优
举下面的例子
3
2 3 6
答案一看就是 2
初始的dancing links的表示是这样的
2 3 6
2 1 0 1
3 0 1 1
6 1 1 1
然后肯定先选第一列进行删
删 第一行时
得到
2 3 6
View Code
2 0 0 0
3 0 1 0
6 0 1 0
此时要么删第二行,要么删第三行,所以6对应的第三行也是可以选的,只是选了第三行,对答案没有影响
#include<cstdio> #include<cstring> #include<queue> #include<cstdlib> #include<algorithm> #include<vector> #include<cmath> using namespace std; typedef long long LL; const int N=1e6+5; const double eps=1e-8; int n,m,sz,k; int u[N],l[N],r[N],d[N]; int h[1005],s[1005],col[N]; void init() { for(int i=0; i<=m; ++i) { s[i]=0; u[i]=d[i]=i; l[i]=i-1; r[i]=i+1; } r[m]=0; l[0]=m; sz=m; for(int i=1; i<=n; ++i) h[i]=-1; } void link(int x,int y) { ++sz; ++s[y],col[sz]=y; u[sz]=u[y],d[u[y]]=sz; d[sz]=y,u[y]=sz; if(h[x]==-1)h[x]=l[sz]=r[sz]=sz; { l[sz]=l[h[x]]; r[l[h[x]]]=sz; r[sz]=h[x]; l[h[x]]=sz; } } void del(int y) { for(int i=d[y]; i!=y; i=d[i]) r[l[i]]=r[i],l[r[i]]=l[i]; } void resume(int y) { for(int i=d[y]; i!=y; i=d[i]) r[l[i]]=l[r[i]]=i; } int f() { int ret=0; for(int i=r[0];i;i=r[i])++ret; return ret; } int ans; void dance(int pos) { if(pos+f()<=ans)return; if(!r[0]) { ans=max(pos,ans); return; } int t=r[0]; for(int i=r[0]; i!=0; i=r[i]) if(s[i]<s[t])t=i; for(int i=d[t]; i!=t; i=d[i]) { del(i); for(int j=r[i]; j!=i; j=r[j]) del(j); dance(pos+1); for(int j=l[i]; j!=i; j=l[j]) resume(j); resume(i); } } LL a[1005]; int main() { int T; scanf("%d",&T); while(T--) { scanf("%d",&n); for(int i=1;i<=n;++i) scanf("%I64d",&a[i]); sort(a+1,a+1+n); m=n; init(); for(int i=1;i<=n;++i) for(int j=1;j<=n;++j) if(a[i]%a[j]==0||a[j]%a[i]==0) link(i,j); ans=0; dance(0); printf("%d ",ans); } return 0; }