2019牛客第5场 Fmaximum clique 1(最大独立集合)

2019牛客第5场 Fmaximum clique 1(最大独立集合(网络流做法和二分图匹配做法))

题目大意

给出n个数字,问一种分法使得可以分出一个尽可能大的集合其中的数字之上二进制位上有两个不同

解题思路

由于数字互不相同,因此至少两位不同的反面就是恰好有一位是不同的。若建图,使得两个数字中恰好有一位不同的点相邻,则只需要找出这个二分图的最大独立集即可。

做法(二分图匹配)

对于最大独立集合,其为最小点覆盖集的反面。而最小点覆盖集的求法就是对二分图中的一遍中未匹配的点以未匹配边开始尽可能连增广路,最后左边的集中被访问过的,右边图中未被访问的就是最小点覆盖集。而这个的反面就是最大独立集合。

AC代码一

#include<bits/stdc++.h>
using namespace std;
const int maxn=5e3+5;
const int maxm=5e5+5;
typedef pair<int,int> pii;
int head[maxn],to[maxm];
int nxt[maxm];
int a[maxn];
inline int lowbit(int x){return x&(-x);}
int n;
bool vis[maxn];
bool color[maxn];
int tot;
int un,vn;
void init()
{
    tot=0;
    memset(head,-1,sizeof(head));
}
void addedge(int u,int v)
{
    to[tot]=v;nxt[tot]=head[u];
    head[u]=tot++;
}
int linker[maxn];
bool used[maxn];
bool dfs(int u)
{
    for(int i=head[u];i!=-1;i=nxt[i])
    {
        int v=to[i];
        if(!used[v])
        {
            used[v]=true;
            if(linker[v]==-1||dfs(linker[v]))
            {
                linker[v]=u;
                return true;
            }
        }
    }
    return false;
}
int hungary()
{
    int res=0;
    memset(linker,-1,sizeof(linker));
    for(int u=1;u<=un;u++)
    {
        if(color[u]!=0) continue;
        memset(used,0,sizeof(used));
        if(dfs(u)) res++;
    }
    return res;
}
 
void colormaker(int u,int col)
{
    vis[u]=true;
    color[u]=col;
    for(int i=head[u];i!=-1;i=nxt[i])
    {
        if(vis[to[i]]) continue;
        colormaker(to[i],~col);
    }
}
vector<int> ans;
queue<pii> q;
void planfinder()
{
    ans.clear();
    memset(vis,0,sizeof(vis));
    for(int i=1;i<=n;i++)
    {
        if(!color[i]) continue;
        if(linker[i]!=-1) continue;
        if(vis[i]) continue;
        while(!q.empty()) q.pop();
        q.push(pii(i,0));
        while(!q.empty())
        {
            int v=q.front().first;
            int sw=q.front().second;
            vis[v]=true;
            q.pop();
            for(int j=head[v];j!=-1;j=nxt[j])
            {
                if(vis[to[j]]) continue;
                if(sw==0) {if(linker[to[j]]!=v) q.push(pii(to[j],1));}
                else {if(linker[to[j]]==v||linker[v]==to[j]) q.push(pii(to[j],0));}
            }
        }
    }
    memset(used,0,sizeof(used));
    for(int i=1;i<=n;i++) if((vis[i]&&(!color[i]))||(!vis[i]&&color[i])) used[i]=true;          
}
int main()
{
    init();
    scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    for(int i=1;i<=n;i++)
    {
        for(int j=i+1;j<=n;j++)
        {
            int sign=a[i]^a[j];
            if(sign==lowbit(sign))
            {
                addedge(i,j);
                addedge(j,i);
            }
        }
    }
    memset(color,0,sizeof(color));
    memset(vis,0,sizeof(vis));
    for(int i=1;i<=n;i++)
    {
        if(!vis[i])colormaker(i,0);
    }
    un=vn=n;
    printf("%d\n",n-hungary());
    planfinder();
    for(int i=1;i<=n;i++)
    {
        if(!used[i])
        printf("%d ",a[i]);
    }
}

做法二(网络流)

对于二分图(U, V) 源连向U,V连向汇。跑完网络流后,残余网络上,从源可达的U中的点,和从源不可达的V中的点,加起来就是最大独立集了

AC代码二

#include<bits/stdc++.h>
using namespace std;
const int maxn=5005;
const int maxm=4e6+5;
const int inf=0x3f3f3f3f;
struct Edge{
    int to,nxt,cap,flow;
}edge[maxm];
int tol;
int head[maxn];
void init(){
    tol=2;
    memset(head,-1,sizeof(head));
}
inline int lowbit(int x){return x&(-x);}
void AddEdge(int u,int v,int w,int rw=0){
    edge[tol].to=v;edge[tol].cap=w;edge[tol].flow=0;
    edge[tol].nxt=head[u];head[u]=tol++;
    edge[tol].to=u;edge[tol].cap=rw;edge[tol].flow=0;
    edge[tol].nxt=head[v];head[v]=tol++;
}
int Q[maxn];
int dep[maxn],cur[maxn],sta[maxn];
bool bfs(int s,int t,int n){
    int front=0,tail=0;
    memset(dep,-1,sizeof(dep[0])*(n+1));
    dep[s]=0;
    Q[tail++]=s;
    while(front<tail){
        int u=Q[front++];
        for(int i=head[u];i!=-1;i=edge[i].nxt){
            int v=edge[i].to;
            if(edge[i].cap>edge[i].flow&&dep[v]==-1){
                dep[v]=dep[u]+1;
                if(v==t) return true;
                Q[tail++]=v;
            }
        }
    }
    return false;
}
int dinic(int s,int t,int n){
    int maxflow=0;
    while(bfs(s,t,n)){
        for(int i=0;i<n;i++) cur[i]=head[i];
        int u=s,tail=0;
        while(cur[s]!=-1){
            if(u==t){
                int tp=inf;
                for(int i=tail-1;i>=0;i--)
                {
                    tp=min(tp,edge[sta[i]].cap-edge[sta[i]].flow);
                }
                maxflow+=tp;
                for(int i=tail-1;i>=0;i--){
                    edge[sta[i]].flow+=tp;
                    edge[sta[i]^1].flow-=tp;
                    if(edge[sta[i]].cap-edge[sta[i]].flow==0) tail=i;
                }
                u=edge[sta[tail]^1].to;
            }
            else if(cur[u]!=-1&&edge[cur[u]].cap>edge[cur[u]].flow&&dep[u]+1==dep[edge[cur[u]].to]){
                sta[tail++]=cur[u];
                u=edge[cur[u]].to;
            }
            else{
                while(u!=s&&cur[u]==-1) u=edge[sta[--tail]^1].to;
                cur[u] = edge [cur[u]].nxt;
            }
        }
    }
    return maxflow;
}
bool color[maxn],vis[maxn];
int n;
int a[maxn];
void dfs2(int v)
{
	vis[v]=true;
	for(int i=head[v];i!=-1;i=edge[i].nxt)
	{
		if(edge[i].cap>edge[i].flow&&!vis[edge[i].to]) dfs2(edge[i].to); 
	}
}
void connect(int u,int v)
{
	if(color[u]>color[v]) swap(u,v);
	AddEdge(u,v,1);
}
int main()
{
	init();
	scanf("%d",&n);
	for(int i=1;i<=n;i++) scanf("%d",&a[i]),color[i]=__builtin_popcount(a[i])%2;
	for(int i=1;i<=n;i++)
	{
		for(int j=i+1;j<=n;j++)
		{
			int sign=a[i]^a[j];
			if(sign==lowbit(sign))
			{
				connect(i,j);
			}
		}
	}
	int s=0,t=n+1;
	for(int i=1;i<=n;i++)
	{
		if(!color[i]) AddEdge(s,i,1);
		else AddEdge(i,t,1);
	}
	printf("%d\n",n-dinic(s,t,t+1));
	memset(vis,0,sizeof(vis));
	dfs2(s);
	for(int i=1;i<=n;i++)
	{
		if(vis[i]) {if(!color[i]) vis[i]=true;else vis[i]=false;}
		else {if(!color[i]) vis[i]=false;else vis[i]=true;}
	}
		
	for(int i=1;i<=n;i++)
	{
		if(vis[i])
		printf("%d ",a[i]);
	}
	puts("");
} 
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值