2019牛客暑期多校训练营(第五场) maximum clique 1

题意:给出n个不相同的数,问选出尽量多的数且任两个数字二进制下不同位数大于等于2。

解法:能想到大于等于2反向思考的话,不难发现这是一个二分图,那么根据原图的最大团等于补图的最大独立点集,此问题就变成 任两个二进制位数相差等于1之间连边(这就是补图),然后求这个图的最大独立点集,仔细观察发现补图是个二分图。那么就可以得到最大独立点集=n-最大匹配。

那么怎么构造一个可行方案呢?参考《算法竞赛进阶指南》的办法,1求出最大匹配  2从左边非匹配点出发跑增广路同时把路上的点标记 3最后左边非匹配点和右边匹配点组成最小点覆盖。    那么最大独立点集就是除了最小点覆盖的点啦。

以前没写过构造最大独立点集,这题写了记录下来以免以后忘了。

#include<bits/stdc++.h>
using namespace std;
const int N=5e3+10;
typedef long long LL;
int n,col[N],match[N],a[N];
map<LL,int> mp;
vector<int> G[N];

void dfs1(int x,int c) {
    col[x]=c;
    for (int i=0;i<G[x].size();i++) {
        int y=G[x][i];
        if (!col[y]) dfs1(y,3-col[x]);
    }
}

bool vis[N],Ans[N];
bool dfs(int x) {
    vis[x]=1;
    for (int i=0;i<G[x].size();i++) {
        int y=G[x][i];
        if (!vis[y]) {
            vis[y]=1;
            if (!match[y] || dfs(match[y])) {
                match[y]=x; match[x]=y;  
                return 1;
            }
        }
    }
    return 0;
}

int main()
{
    scanf("%d",&n);
    for (int i=1;i<=n;i++) scanf("%d",&a[i]);
    for (int i=0;i<=33;i++) mp[1LL<<i]=1;
    for (int i=1;i<=n;i++)
        for (int j=i+1;j<=n;j++)
            if (mp.count(a[i]^a[j])) G[i].push_back(j),G[j].push_back(i);
    for (int i=1;i<=n;i++)
        if (!col[i]) dfs1(i,1);         
    
    int ans=0;
    for (int i=1;i<=n;i++) {
        if (col[i]==2) continue;
        memset(vis,0,sizeof(vis));
        if (dfs(i)) ans++;
    }    
    cout<<n-ans<<endl;
    
    memset(vis,0,sizeof(vis));
    for (int i=1;i<=n;i++)
        if (col[i]==1 && !match[i]) dfs(i);
    memset(Ans,0,sizeof(Ans));    
    for (int i=1;i<=n;i++)
        if (col[i]==1 && !vis[i] || col[i]==2 && vis[i]) Ans[i]=1;
    for (int i=1;i<=n;i++)
        if (!Ans[i]) printf("%d ",a[i]);            
    return 0;
} 

猜你喜欢

转载自www.cnblogs.com/clno1/p/11438094.html