题目:
题目链接:[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;
}