2018.10.11【洛谷P4298】【CTSC2008】祭祀(二分图最大独立集)(最长反链)

传送门


解析:

本来各大 O J OJ OJ上这道题不需要输出方案的。虽然说原题需要

然后洛谷上的 d a l a o dalao dalao果断的给出了 S P J SPJ SPJ。。。

于是就跑去学习了一下怎么输出方案。

显然第一个方案就是要求我们构造一个最大独立集。
显然考虑做最小点覆盖,即最大独立集的补集。

我们这样做:先找一个最大匹配,然后从左边点集中每一个没有被匹配到的点做假的增广,一路标记增广到的点。最后左边没有标记过的或右边标记过的就是最小点覆盖。取反就是最大独立集。

而第二个问要求哪些点能够出现在最大独立集里面。

其实这个做法有点巧妙,
枚举我们要考虑的点,删去它以及它的前驱后继,就是它能到和能到它的点。
然后再求一次最长反链。

如果说这样求出来的最长反链比原来的小1,那么选了他就可以替换掉原来的最长反链中的一个点(可能本来就是它自己)而保持最长反链的性质,它就能够出现在最长反链中。

就相当于是我们先在一条反链中选择了这个点,我们就不能选择它的相邻点,然而如果这样求出来剩余点集的最长反链只比原来少 1 1 1,那么加上这个点就能够形成我们已经求出的最长反链。


代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define re register
#define gc getchar
#define pc putchar
#define cs const

inline int getint(){
	re int num;
	re char c;
	while(!isdigit(c=gc()));num=c^48;
	while(isdigit(c=gc()))num=(num<<1)+(num<<3)+(c^48);
	return num;
}

cs int N=105;
int n;
bool g[N][N],to[N];
int match[N],idx;
int vis[N];
bool ban[N],s[N],t[N];

inline bool find(int u){
	if(ban[u])return false;
	for(int re v=1;v<=n;++v){
		if((vis[v]^idx)&&g[u][v]&&!ban[v]){
			vis[v]=idx;
			if(!match[v]||find(match[v])){
				to[u]=true,match[v]=u;
				return true;
			}
		}
	}
	return false;
}

inline void dfs(int u){
	if(s[u])return ;
	s[u]=true;
	for(int re v=1;v<=n;++v){
		if(g[u][v]&&!t[v])t[v]=true,dfs(match[v]);
	}
}

int m;
signed main(){
	n=getint(),m=getint();
	for(int re i=1;i<=m;++i){
		int u=getint(),v=getint();
		g[u][v]=1;
	}
	
	for(int re k=1;k<=n;++k)
	for(int re i=1;i<=n;++i)
	for(int re j=1;j<=n;++j)
	g[i][j]=g[i][j]||(g[i][k]&&g[k][j]);
	
	int ans=n;
	for(int re i=1;i<=n;++i){
		++idx;
		if(find(i))--ans;
	}
	printf("%d\n",ans);
	
	for(int re i=1;i<=n;++i)if(!to[i])dfs(i);
	for(int re i=1;i<=n;++i)printf("%d",s[i]&&!t[i]);
	pc('\n');
	
	for(int re k=1;k<=n;++k){
		memset(match,0,sizeof match);
		memset(to,0,sizeof to);
		memset(ban,0,sizeof ban);
		int tmp=0;
		
		for(int re i=1;i<=n;++i)
		if(g[i][k]||g[k][i]||i==k)ban[i]=true;
		else ++tmp;
		
		for(int re i=1;i<=n;++i){
			++idx;
			if(find(i))--tmp;
		}
		printf("%d",tmp==ans-1);
		
	}
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值