HDU 3335 Divisibility(Dilworth定理+最小路径覆盖)

87 篇文章 0 订阅

Divisibility

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 2043    Accepted Submission(s): 823


Problem Description
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.
 

Author
DaiZhenyang@BUPT
 

Source
 

Recommend
lcy

题目大意:

    给你一些数字,让你从中选择尽可能多的数字,满足它们任意两个存在整除关系。


解题思路:

    最容易想到的就是把数字看做点,把存在整除关系的点之间连上无向边,求最大独立集。可是无向图的最大独立集是NP问题,我们还需要继续思考。可以发现整除关系满足偏序关系,这时就可以利用考虑使用Dilworth定理。


    Dilworth定理应用到图论中就变成了:对于满足偏序关系(自反,反对称,传递)的有向图,最大独立集等于最小路径覆盖。

    所以我们就可以根据整除关系建立有向图,这个DAG的最小路径覆盖即为答案。

    有一点需要注意的是,由于有向图最小路径覆盖要求图是DAG,而这里是可以出现环的,所以我们要手动去掉环,具体操作就是对给出的序列去重,建边时不要建立自环。


AC代码:

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <vector>
#include <queue>
#include <stack>
#include <cmath>
#include <cstdlib>
#include <map>
#include <string>
using namespace std;
#define INF 0x3f3f3f3f
#define LL long long
#define mem(a,b) memset((a),(b),sizeof(a))

const int MAXV=1000+3;
int V;
LL a[MAXV];
int num_x;//左边顶点数
vector<int> G[MAXV];//图的邻接表形式(左边顶点在前面,只需要建立从左边指向右边的边即可)
int match_x[MAXV],match_y[MAXV];//顶点匹配对象
int dis,dis_x[MAXV],dis_y[MAXV];//距离(用于多路增广)
bool used[MAXV];

bool searchP()//标记距离(用于多路增广)
{
    queue<int> que;
    dis=INF;
    mem(dis_x,-1);
    mem(dis_y,-1);
    for(int i=0;i<num_x;++i)
        if(match_x[i]==-1)
        {
            que.push(i);
            dis_x[i]=0;
        }
    while(!que.empty())
    {
        int u=que.front(); que.pop();
        if(dis_x[u]>dis)
            break;
        for(int i=0;i<G[u].size();++i)
        {
            int v=G[u][i];
            if(dis_y[v]==-1)
            {
                dis_y[v]=dis_x[u]+1;
                if(match_y[v]==-1)
                    dis=dis_y[v];
                else
                {
                    dis_x[match_y[v]]=dis_y[v]+1;
                    que.push(match_y[v]);
                }
            }
        }
    }
    return dis!=INF;
}

bool dfs(int u)//dfs增广
{
    for(int i=0;i<G[u].size();++i)
    {
        int v=G[u][i];
        if(!used[v]&&dis_y[v]==dis_x[u]+1)
        {
            used[v]=true;
            if(match_y[v]!=-1&&dis_y[v]==dis)
                continue;
            if(match_y[v]==-1||dfs(match_y[v]))
            {
                match_y[v]=u;
                match_x[u]=v;
                return true;
            }
        }
    }
    return false;
}

int hopcroft_carp()
{
    int res=0;
    mem(match_x,-1);
    mem(match_y,-1);
    while(searchP())
    {
        mem(used,0);
        for(int i=0;i<num_x;++i)
            if(match_x[i]==-1&&dfs(i))
                ++res;
    }
    return res;
}

void init()
{
    for(int i=0;i<V;++i)
        G[i].clear();
}

int main()
{
    int T_T;
    scanf("%d", &T_T);
    while(T_T--)
    {
        scanf("%d", &V);
        num_x=V;
        init();
        for(int i=0;i<V;++i)
            scanf("%lld", &a[i]);
        sort(a, a+V);//排序方便去重
        int cut=0;//统计重复的数字数
        for(int i=0;i<V;++i)
        {
            if(i && a[i]==a[i-1])
            {
                ++cut;
                continue;
            }
            for(int j=0;j<V;++j)
            {
                if(j && a[j]==a[j-1])
                    continue;
                if(i!=j && a[i]%a[j]==0)
                    G[j].push_back(i);
            }
        }
        printf("%d\n", V-cut-hopcroft_carp());
    }
    
    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值