本来各大 O J OJ OJ上这道题不需要输出方案的。虽然说原题需要
然后洛谷上的 d a l a o dalao dalao果断的给出了 S P J SPJ SPJ。。。
于是就跑去学习了一下怎么输出方案。
显然第一个方案就是要求我们构造一个最大独立集。
显然考虑做最小点覆盖,即最大独立集的补集。
我们这样做:先找一个最大匹配,然后从左边点集中每一个没有被匹配到的点做假的增广,一路标记增广到的点。最后左边没有标记过的或右边标记过的就是最小点覆盖。取反就是最大独立集。
而第二个问要求哪些点能够出现在最大独立集里面。
其实这个做法有点巧妙,
枚举我们要考虑的点,删去它以及它的前驱后继,就是它能到和能到它的点。
然后再求一次最长反链。
如果说这样求出来的最长反链比原来的小1,那么选了他就可以替换掉原来的最长反链中的一个点(可能本来就是它自己)而保持最长反链的性质,它就能够出现在最长反链中。
就相当于是我们先在一条反链中选择了这个点,我们就不能选择它的相邻点,然而如果这样求出来剩余点集的最长反链只比原来少 1 1 1,那么加上这个点就能够形成我们已经求出的最长反链。
代码:
#include
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;
}