题意:

一个可重集合中包含n个元素 ,你需要选择一个子集,使得这个子集中任意两个元素 均满足条件,且这个子集的元素个数是所有满足上述条件的子集中最多的。输出这个子集的元素个数。

思路:

挺经典的一道题。

看这道题就可以想到最大团。

最大团就是图的最大的完全子图(每两个点之间都有边相连)。

这里满足 的每两个都用边相连,该图的最大团就是题目所求了。

如何求最大团呢?

最大团=补图的最大独立集。最大独立集数目=|V|−最大匹配数。

该图的补图就是对满足的每两个用边相连。

然后二分图匹配就可以了。

有一个小优化,奇数和奇数,偶数和偶数肯定是不用连边的,只要连奇数和偶数就好了。

看别人还有一种做法,多次random_shuffle取满足条件的一个一个加到答案里。

以后如果看到两两满足条件这种题目的的话,可以试着从这种角度来做。

代码:

int n;
ll a[510];
vector<int>v[510];
bool vis[510];
int link[510];
ll gcd(ll a,ll b)
{
    if(a<b){ll temp;temp=a;a=b;b=temp;}
    while(a%b){ll r=a%b;a=b;b=r;}
    return b;
}
bool judge(int id)
{
    for(int i=0;i<v[id].size();i++)
    {
        int temp=v[id][i];
        if(!vis[temp])
        {
            vis[temp]=true;
            if(link[temp]==-1||judge(link[temp]))
            {
                link[temp]=id;
                return true;
            }
        }
    }
    return false;
}
int maxmatch()
{
    memset(link,-1,sizeof(link));
    int num=0;
    for(int i=1;i<=n;i++)
    {
        memset(vis,0,sizeof(vis));
        if(judge(i))
            num++;
    }
    return num;
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%lld",&a[i]);
    for(int i=1;i<=n;i++)
        for(int j=i;j<=n;j++)
        {
            if(gcd(a[i],a[j])*gcd(a[i]+1,a[j]+1)==1)
            {
                v[i].push_back(j);
                v[j].push_back(i);
            }
        }
    printf("%d\n",n-maxmatch()/2);
    return 0;
}

本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!

调试用 Previous
金马五校赛C 得到目标数字的最小操作数 Next