【二分图】[LUOGU P4298 祭祀] 最长反链+输出方案

题目:

题目链接:[LUOGU P4298 祭祀]
题意:求最长反链的长度并输出方案。
题解:
这个题最重要的结论:
最长反链=最小点覆盖数=最小路径覆盖数=总点数-最大匹配
然后就可以进行对输出方案的处理了,处理的时候因为已经找到了一条反链了,现在就是对于整个图判断各个顶点,先在一条反链中选择了这个点,就不能选择它的相邻点,然而如果这样求出来剩余点集的最长反链只比原来少1,那么加上这个点就能够形成我们已经求出的最长反链。
重点看注释吧,,,

代码:

#include<bits/stdc++.h>
using namespace std;
inline int read()
{
	int s=0,w=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
	while(ch<='9'&&ch>='0')s=s*10+ch-'0',ch=getchar();
	return s*w;
}
const int sea=110;
int n,m,id,a[sea][sea],pre[sea],v[sea],s[sea],vv[sea],t[sea],to[sea];
bool hgry(int x)
{
	if(vv[x]) return 0; 
	for(int i=1;i<=n;i++)
	if((v[i]^id)&&a[x][i]&&!vv[i])
	{
		v[i]=id;
		if(!pre[i]||hgry(pre[i]))
		{to[x]=1;pre[i]=x;return 1;}
	}
	return 0;
}
void dfs(int x)
{
	if(s[x]) return ;s[x]=1;
	for(int y=1;y<=n;y++)
	if(a[x][y]&&!t[y]) t[y]=1,dfs(pre[y]);
}
int main()
{
	n=read(); m=read();
	for(int i=1;i<=m;i++)
	{
		int x=read(),y=read();
		a[x][y]=1;
	} 
	//Floyed求传递闭包
	for(int k=1;k<=n;k++)
	for(int i=1;i<=n;i++)
	for(int j=1;j<=n;j++)
	a[i][j]=a[i][j]||(a[i][k]&&a[k][j]);
	int ans=0;
	for(int i=1;i<=n;i++) {++id;if(hgry(i)) ans++;}
	printf("%d\n",n-ans);
//	int ans=n;
//	for(int i=1;i<=n;i++) {++id;if(hgry(i)) ans--;}
//	printf("%d\n",ans);
	//不知道为什么这样写就错了,还WA掉我28分,求大佬帮忙
	for(int i=1;i<=n;i++) if(!to[i]) dfs(i);
	for(int i=1;i<=n;i++) printf("%d",s[i]&&!t[i]); putchar('\n');//找到一条最长反链
	//确定哪些点能够出现在最大独立集里面
	for(int k=1;k<=n;k++)
	{
		memset(vv,0,sizeof(vv));
		memset(pre,0,sizeof(pre));
		memset(to,0,sizeof(to));
		int s=0;
		for(int i=1;i<=n;i++)
		if(a[i][k]||a[k][i]||i==k) vv[i]=1;else ++s;//删去它以及它的前驱后继,这样就比较好逐个判断是否是在最大独立集里,但是如果他在里面那么,删去了之后就会发现再次跑的最大独立集就少了一,如果不在,那就不等于就好。
		for(int i=1;i<=n;i++) {++id;if(hgry(i)) --s;}
		printf("%d",s==ans-1); //判断他是否在最大独立集里
	}
	return 0;
} 

Continue……

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值