HDU 3335 Divisibility(DLX求最多重复覆盖)

135 篇文章 0 订阅
109 篇文章 0 订阅

题目地址
题意:告诉你n个数,问你最多可以得到多少个数,当你选择了这个数的时候,可以与他构成整除关系的数就不能选了。
思路:我们构造一个矩阵,我们枚举每对数,当这两个数满足整除关系的时候就让第i行第j列和第j行第i列的值置为1,这样的话就表示一个选了就不能选了。

#include <iostream>
#include <cstring>
#include <string>
#include <queue>
#include <vector>
#include <map>
#include <set>
#include <cmath>
#include <cstdio>
#include <algorithm>
#include <iomanip>
#define N 1010
#define M 110
#define MAXSIZE 1010*1010
#define LL __int64
#define inf 0x3f3f3f3f
#define lson l,mid,ans<<1
#define rson mid+1,r,ans<<1|1
#define getMid (l+r)>>1
#define movel ans<<1
#define mover ans<<1|1
using namespace std;
const LL mod = 1000000007;
const double eps = 0.001;
struct node {
    int left, right, up, down, col, row;
}mapp[MAXSIZE];
int S[N], H[N];//S记录该列中1元素的个数
int head, cnt;
int len;
struct Dancing_Links_X {
    void init(int m) {
        head = 0;
        for (int i = 0; i <= m; i++) {
            S[i] = 0;
            mapp[i].up = mapp[i].down = i;
            mapp[i].left = (i == 0 ? m : i - 1);
            mapp[i].right = (i == m ? 0 : i + 1);
        }
        cnt = m;
        memset(H, -1, sizeof(H));
    }
    void link(int x, int y) {
        cnt++;
        mapp[cnt].row = x;
        mapp[cnt].col = y;
        S[y]++;
        mapp[cnt].up = mapp[y].up;
        mapp[cnt].down = y;
        mapp[mapp[y].up].down = cnt;
        mapp[y].up = cnt;
        if (H[x] == -1) H[x] = mapp[cnt].left = mapp[cnt].right = cnt;
        else {
            mapp[cnt].left = mapp[H[x]].left;
            mapp[cnt].right = H[x];
            mapp[mapp[H[x]].left].right = cnt;
            mapp[H[x]].left = cnt;
        }
    }
    void remove(int c) {//删去c这个点,以及关联的一列
        for (int i = mapp[c].down; i != c; i = mapp[i].down) {
            mapp[mapp[i].left].right = mapp[i].right;
            mapp[mapp[i].right].left = mapp[i].left;
        }
    }
    void resume(int c) {//恢复c这个点,以及关联的一列
        for (int i = mapp[c].up; i != c; i = mapp[i].up) {
            mapp[mapp[i].left].right = i;
            mapp[mapp[i].right].left = i;
        }
    }
    bool used[N];
    int h() {
        int sum = 0;
        for (int i = mapp[head].right; i != head; i = mapp[i].right) used[i] = false;
        for (int i = mapp[head].right; i != head; i = mapp[i].right) {
            if (!used[i]) {
                sum++;
                used[i] = true;
                for (int j = mapp[i].down; j != i; j = mapp[j].down)
                    for (int k = mapp[j].right; k != j; k = mapp[k].right) 
                        used[mapp[k].col] = true;
            }
        }
        return sum;
    }
    void dance(int nums) {
        if (nums + h() <= len) return;//求最大的覆盖数
        if (mapp[head].right == head) {
            len = max(len, nums);
            return;
        }
        int s = inf, c;
        for (int t = mapp[head].right; t != head; t = mapp[t].right) {
            if (S[t] < s) s = S[t], c = t;
        }
        for (int i = mapp[c].down; i != c; i = mapp[i].down) {
            remove(i);
            for (int j = mapp[i].right; j != i; j = mapp[j].right) {
                remove(j);
            }
            dance(nums + 1);
            for (int j = mapp[i].left; j != i; j = mapp[j].left) {
                resume(j);
            }
            resume(i);
        }
        return;
    }
}DLX;
int main() {
    int n;
    int T;
    LL num[N];
    scanf("%d", &T);
    while (T--) {
        scanf("%d", &n);
        for (int i = 1; i <= n; i++) {
            scanf("%lld", &num[i]);
        }
        DLX.init(n);
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= i; j++) {
                if (num[i] % num[j] == 0 || num[j] % num[i] == 0) {
                    DLX.link(i, j);
                    DLX.link(j, i);
                }
            }
        }
        len = 0;
        DLX.dance(0);
        printf("%d\n", len);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值