Dancing Link------G - Divisibility

本文介绍了一种解决数论问题的算法,特别是关于数字的可除性和选择最大数量不可互除的正整数的问题。通过使用深度搜索和精确覆盖的技术,文章详细解释了如何构建和优化解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

As we know,the fzu AekdyCoin is famous of math,especially in the field of number theory.So,many people call him “the descendant of Chen Jingrun”,which brings him a good reputation.
AekdyCoin also plays an important role in the ACM_DIY group,many people always ask him questions about number theory.One day,all members urged him to conduct a lesson in the group.The rookie daizhenyang is extremely weak at math,so he is delighted.
However,when AekdyCoin tells us “As we know, some numbers have interesting property. For example, any even number has the property that could be divided by 2.”,daizhenyang got confused,for he don’t have the concept of divisibility.He asks other people for help,first,he randomizely writes some positive integer numbers,then you have to pick some numbers from the group,the only constraint is that if you choose number a,you can’t choose a number divides a or a number divided by a.(to illustrate the concept of divisibility),and you have to choose as many numbers as you can.
Poor daizhenyang does well in neither math nor programming.The responsibility comes to you!
Input
An integer t,indicating the number of testcases,
For every case, first a number n indicating daizhenyang has writen n numbers(n<=1000),then n numbers,all in the range of (1…2^63-1).
Output
The most number you can choose.
Sample Input
1
3
1 2 3
Sample Output
2

Hint:
If we choose 2 and 3,one is not divisible by the other,which is the most number you can choose.

我原先觉得01矩阵行和列也是给出的每个数,但是精确覆盖不可能啊,重复覆盖的话,像样例那样选择1和2也是可以的,但是和题意不相符,1和2是不可以被同时选的。。。。
但是题里面只要求个数,这样找出来并不影响答案的正确性。。。。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<map>
#include<queue>
#include<cmath>
using namespace std;
typedef long long LL;
const int inf = 0x3f3f3f3f3f;
const double eps = 1e-8;
const double PI = acos(-1);
#define pb push_back
#define mp make_pair
#define fi first
#define se second

//最大行数
const int MN = 1005;
//最大列数
const int MM = 1005;
//最大点数
const int MNN = 1e5 + 5 + MM;

struct DLX
{
    //一共n行m列,s个节点
    int n,m,s;
    //交叉十字链表组成部分
    //第i个节点的上U下D左L右R,所在位置row行col列
    int U[MNN],D[MNN],L[MNN],R[MNN],row[MNN],col[MNN];
    //H数组记录行选择指针,S数组记录覆盖个数
    int H[MN],S[MM];
    //res记录行个数,ans数组记录可行解
    int res,ans[MN];
    //初始化空表
    void init(int x,int y)
    {
        n = x,m = y;
        //其中0节点作为head节点,其他作为列首节点
        for(int i = 0;i <= m;++i){
            U[i] = D[i] = i;
            L[i] = i - 1;
            R[i] = i + 1;
        }
        R[m] = 0;L[0] = m;
        s = m;
        memset(S,0,sizeof(S));
        memset(H,-1,sizeof(H));
    }
    void Insert(int r,int c)
    {
        //节点数加一,设置s节点所处位置,以及S列覆盖个数加一
        s++;row[s] = r;col[s] = c;S[c]++;
        //将s节点插入对应列中
        D[s] = D[c];U[D[c]] = s;
        U[s] = c;D[c] = s;
        if(H[r] < 0){//如果该行没有元素,H[r]标记该行起始节点
            H[r] = L[s] = R[s] = s;
        }else{
            //将该节点插入该行第一个节点后面
            R[s] = R[H[r]];
            L[R[H[r]]] = s;
            L[s] = H[r];
            R[H[r]] = s;
        }
    }
    //精确覆盖
    void Remove(int c)
    {
        //删除c列
        L[R[c]] = L[c];R[L[c]] = R[c];
        //删除该列上的元素对应的行
        for(int i = D[c];i != c;i = D[i]){//枚举该列元素
            for(int j = R[i];j != i;j = R[j]){//枚举列的某个元素所在行遍历
                U[D[j]] = U[j];
                D[U[j]] = D[j];
                //将该列上的S数组减一
                --S[col[j]];
            }
        }
    }
    void resume(int c)
    {
        //恢复c列
        for(int i = U[c];i != c;i = U[i]){//枚举该列元素
            for(int j = L[i];j != i;j = L[j]){
                U[D[j]] = j;D[U[j]] = j;
                ++S[col[j]];
            }
        }
        L[R[c]] = c;R[L[c]] = c;
    }
    bool dance(int deep)
    {
        if(res < deep) return false;
        //当矩阵为空时,说明找到一个可行解,算法终止
        if(R[0] == 0){
            res = min(res,deep);
            return true;
        }
        //找到节点数最少的列,枚举这列上的所有行
        int c = R[0];
        for(int i = R[0];i != 0;i = R[i]){
            if(S[i] < S[c]){
                c = i;
            }
        }
        //删除节点数最少的列
        Remove(c);
        for(int i = D[c];i != c;i = D[i]){
            //将行r放入当前解
            ans[deep] = row[i];
            //行上节点对应的列上进行删除
            for(int j = R[i];j != i;j = R[j])
                Remove(col[j]);
            //进入下一层
            dance(deep + 1);
            //对行上的节点对应的列进行恢复
            for(int j = L[i];j != i;j = L[j])
                resume(col[j]);
        }
        //恢复节点数最少列
        resume(c);
        return false;
    }

    //重复覆盖
    //将列与矩阵完全分开
    void Remove1(int c)
    {
        for(int i = D[c];i != c;i = D[i]){
            L[R[i]] = L[i];
            R[L[i]] = R[i];
        }
    }
    void resume1(int c)
    {
        for(int i = D[c];i != c;i = D[i]){
            L[R[i]] = R[L[i]] = i;
        }
    }
    int vis[MNN];
    //估价函数,模拟删除列,H(),函数返回的是至少还需要多少行才能完成重复覆盖
    int A()
    {
        int dis = 0;
        for(int i = R[0];i != 0;i = R[i]) vis[i] = 0;
        for(int i = R[0];i != 0;i = R[i]){
            if(!vis[i]){
                dis++;vis[i] = 1;
                for(int j = D[i];j != i;j = D[j]){
                    for(int k = R[j];k != j;k = R[k]){
                        vis[col[k]] = 1;
                    }
                }
            }
        }
        return dis;
    }

    void dfs(int deep)
    {
        if(!R[0]){
            //cout << res << endl;
            res = max(res,deep);
            return ;
        }
        //if(deep + A() >= res) return ;
        int c = R[0];
        for(int i = R[0];i != 0;i = R[i]){
            if(S[i] < S[c]){
                c = i;
            }
        }
        for(int i = D[c];i != c;i = D[i]){
            //每次将第i列其他节点删除,只保留第i节点,为了找该行的节点
            Remove1(i);
            //将列上的节点完全与矩阵脱离,只删列首节点是不行的
            for(int j = R[i];j != i;j = R[j]){
                Remove1(j);
            }
            dfs(deep + 1);
            for(int j = L[i];j != i;j = L[j]){
                resume1(j);
            }
            resume1(i);
        }
    }
}dlx;

const int N = 1005;
LL num[N];
int n;

int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d",&n);
        dlx.init(n,n);
        for(int i = 1;i <= n;++i)
            scanf("%lld",&num[i]);
        for(int i = 1;i <= n;++i){
            for(int j = 1;j <= n;++j){
                if(num[j] % num[i] == 0 || num[i] % num[j] == 0){
                        //cout << i << " " << j << endl;
                        dlx.Insert(i,j);
                }
            }
        }
        dlx.res = 0;
        dlx.dfs(0);
        printf("%d\n",dlx.res);
    }
    return 0;
}

### H-C定理及其在计算机科学中的数学证明 H-C定理通常指的是 **霍尔德-卡普兰定理(Holder-Kaplan Theorem)** 或其他类似的理论名称,具体取决于上下文。然而,在当前引用的内容中并未提及该特定定理的定义或背景[^1]。因此,以下是基于一般性的假设以及已知的数学和计算机科学领域内的相关内容展开讨论。 #### 什么是H-C定理? 如果我们将“H-C定理”理解为一种涉及代数结构、组合分析或者计算复杂度的命题,则可以推测其可能属于形式化方法的一部分。例如,某些定理可以通过自动定理证明器来验证真伪性。这种技术依赖于逻辑推导规则,并利用算法完成复杂的演绎过程[^2]。 对于任何给定的数学陈述而言,“proposition”的本质是一个布尔值表达式——即要么是真的,要么是假的。这正是现代计算机辅助证明的核心理念之一:通过程序化的手段确认这些断言的有效性[^3]。 #### 自动定理证明的应用 当提到用计算机来进行定理证明时,实际上是指借助软件工具执行一系列严格的推理步骤直至得出结论为止。这种方法不仅适用于简单的初等几何问题,还可以处理高度抽象的概念比如群论、拓扑学等领域里的难题。历史上著名的例子包括四色地图着色猜想最终由Appel与Haken借助大量运算才得以解决的情况。 值得注意的是,尽管自动化系统能够高效地检验已有成果的真实性,但对于揭示隐藏规律或者说启发新思路方面仍然存在不足之处。正如归纳法所展示出来的那样,虽然它可以强有力地支持某个特定模式下的普遍适用情况,但却无法阐明背后深层次的原因所在。 ```python def check_divisibility(n): """Check whether the expression n^3 - n is divisible by 3.""" result = (n**3 - n) % 3 == 0 return result # Example usage of function to demonstrate divisibility property. for i in range(-5, 6): print(f"For n={i}, {check_divisibility(i)}") ``` 上述代码片段展示了如何编程实现一个简单性质测试函数,用于判断整数`n`满足条件\(n^{3}-n\)能被三整除的情形。这是运用基本原理构建实际解决方案的一个实例说明。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值