2019牛客暑期多校第五场 F.maximum clique 1 (二分图最大独立集)

本文探讨了如何通过转换二分图最大独立集问题为最大匹配问题来解决特定的数学挑战。通过巧妙地建立图模型,将数字的二进制特性与图论结合,提出了一种有效的算法解决方案。

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

题意:给你N个点,让你求一个最大集合,使得集合内任意两个数字在二进制下都有两个或两个以上的位置不同。

题解:

  • 首先明白 二分图的最大独立集问题和最大团问题是互补的关系。
  • “让你求一个最大集合,使得集合内任意两个数字在二进制下都有两个或两个以上的位置不同”这就可以看成是最大团问题。
  • 所以 相反考虑的话 就是求 “两个数字在二进制下只有一位不同” 的 问题, 那么最后这两数字肯定只取一个。就可以转换成二分图最大匹配了。进而考虑最大独立集。   【建图思想
  • 二分图最大独立集=N-二分图最大匹配(实质是最大匹配的边)
  • 最小点覆盖的点集=最大匹配数
  • 至于建图,把两点二进制只有一位1不同的两点分别放在左集和右集中,那么最后这两侧匹配的数字肯定只取一侧的,左集右集也符合二分图建图。
  • 细节就是把奇数1的放在左集,偶数1的放在右集。
  •  最小点覆盖覆盖到的点的计算方法最终左边未被标记的点,与右边被标记的点就是覆盖的点集 (恰好是每条匹配边的一个端点) 如果是拿dinic分层跑的话,左部的点就是dis[]为-1的点,右部就是dis[]不为-1的点。 最大独立集的点与最小点覆盖恰好相反,也就是最小点覆盖的补图就是最大独立集。

第二问我是dlc网络流跑二分图,所以最大独立集的点就等于 左部被标记的点(dis[]!=-1)和右部未被标记的点(dis[]=1)

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn = 5e3+5;//点 
const int N = 3e7+5;//边 
const int INF = 0x3f3f3f3f;
const ll MOD = 1e9 + 7;
int a[maxn];
int oddvis[maxn];
bool vis[maxn];
struct Edge{
    int to;
    int cap, flow;
    int next;
}edge[N];
int cnt;
int head[maxn];
int dis[maxn];
int cur[maxn];
void add(int u, int v, int w){
    edge[cnt] = (struct Edge){v, w, 0, head[u]};
    head[u] = cnt++;
    edge[cnt] = (struct Edge){u, 0, 0, head[v]};
    head[v] = cnt++;
}
bool bfs(int start, int endd){//分层
    memset(dis, -1, sizeof(dis));
    memset(vis, false, sizeof(vis));
    queue<int>que;
    dis[start] = 0;
    vis[start] = true;
    que.push(start);
    while(!que.empty()){
        int u = que.front();
        que.pop();
        for(int i = head[u]; i != -1; i = edge[i].next){
            Edge E = edge[i];
            if(!vis[E.to] && E.flow<E.cap){
                dis[E.to] = dis[u]+1;
                vis[E.to] = true;
                if(E.to == endd) return true;
                que.push(E.to);
            }
        }
    }
    return false;
}
int dfs(int x, int res, int endd){ //增广
    if(x == endd || res == 0) return res;
    int flow = 0, f;
    for(int& i = cur[x]; i != -1; i = edge[i].next){
        Edge E = edge[i];
        if(dis[E.to] == dis[x]+1){
            f = dfs(E.to, min(res, E.cap-E.flow), endd);
            if(f>0){
                edge[i].flow += f;
                edge[i^1].flow -= f;
                flow += f;
                res -= f;
                if(res == 0) break;
            }
        }
    }
    return flow;
}
int max_flow(int start, int endd){
    int flow = 0;
    while(bfs(start, endd)){
        memcpy(cur, head, sizeof(head));
        flow += dfs(start, INF, endd);
    }
    return flow;
}
void init(){//初始化
    cnt = 0;
    memset(head, -1, sizeof(head));
}
int getlen(int x){
    int len=0;//1的个数
    while(x){
       // if(x%2) len++;
        //x=x/2;
        len++;
        x-=(x&-x);
    }
    return len;
}
int main(){
    std::ios::sync_with_stdio(false);
    int n;
    cin>>n;
    int s=0;//建边 左奇右偶
    int e=n+1;
    init();
    for(int i=1;i<=n;i++)  cin>>a[i];
    for(int i=1;i<=n;i++){
        int tmp=a[i];
        int len=getlen(tmp);
        if(len&1) oddvis[i]=1,add(s,i,1);
        else add(i,e,1);
    }
    for(int i=1;i<=n;i++){
        int na=a[i];
        for(int j=i+1;j<=n;j++){
            int nb=a[j];
            int nab=na^nb;//找两数相差只有一个1的
            if(getlen(nab)==1){
                if(oddvis[i]) add(i,j,1);
                else add(j,i,1);
            }
        }
    }
    int num=max_flow(s,e);//二分图最大匹配 
    cout<<n-num<<endl;//二分图最大独立集 
    for(int i=1;i<=n;i++)//最大独立集的点 
      if(oddvis[i]&&dis[i]!=-1) cout<<a[i]<<" ";
        else if(oddvis[i]==0&&dis[i]==-1) cout<<a[i]<<" ";
    cout<<endl;
     
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值