传递闭包跑一遍按联通建图
$(1)$最长反链长度=最小链覆盖=n-最大匹配
$(2)$定义作为最大匹配出现在左端点的集合为$S$,作为最大匹配出现在右端点的集合为$T$
定义函数$ft(x)$为$S$中任意点在$T$中的对应点,定义函数$fs(s)$为$T$中任意点在$S$中的对应点
先找最大匹配,从$S$的补集出发增广,当然这里增广不是真正意义上的增广,只是遍历并标记经过的点而已
左边没有标记过的或右边标记过的就是最小点覆盖,也就是最大独立集的补集
$(3)$枚举点,每次删掉在跑匹配就行
My complete code:
#include<cstdio> #include<cstring> #include<iostream> using namespace std; struct node{ int to,next; }edge[11000]; int n,m,ans,link,num,cnt,idx,ci; int dis[210][210],head[210],mat[210],to[210],s[210],t[210],visit[210]; bool del[210]; inline void add(int u,int v){ edge[++cnt]=(node){v,head[u]}; head[u]=cnt; } bool dfs(int u){ if(del[u]) return false; for(int i=head[u];i;i=edge[i].next){ int v=edge[i].to; if(visit[v]!=idx&&!del[v]){ visit[v]=idx; if(!mat[v]){ to[u]=v; mat[v]=u; return true; }else if(dfs(mat[v])){ to[u]=v; mat[v]=u; return true; } } } return false; } void cal(int u){ if(s[u]) return; s[u]=1; for(int i=head[u];i;i=edge[i].next){ int v=edge[i].to; if(t[v]) continue; t[v]=1; cal(mat[v]); } } int main(){ scanf("%d%d",&n,&m); for(int i=1;i<=m;++i){ int u,v; scanf("%d%d",&u,&v); dis[u][v]=1; } for(int k=1;k<=n;++k) for(int i=1;i<=n;++i) for(int j=1;j<=n;++j) dis[i][j]|=(dis[i][k]&dis[k][j]); for(int i=1;i<=n;++i) for(int j=1;j<=n;++j) if(dis[i][j]) add(i,j); ans=n; idx=0; memset(del,false,sizeof(del)); for(int i=1;i<=n;++i){ ++idx; if(dfs(i)) ans--; } printf("%d ",ans); for(int i=1;i<=n;++i) if(!to[i]) cal(i); for(int i=1;i<=n;++i) printf("%d",!t[i]&&s[i]);puts(""); for(int i=1;i<=n;++i){ memset(del,false,sizeof(del)); memset(mat,0,sizeof(mat)); memset(visit,0,sizeof(visit)); int nn=0; for(int j=1;j<=n;++j) if(dis[i][j] || j==i || dis[j][i]) del[j]=true; else nn++; idx=0; for(int j=1;j<=n;++j) if(!del[j]){ ++idx; if(dfs(j)) nn--; } if(nn==ans-1) printf("1"); else printf("0"); } return 0; }