版权声明:虽然博主很菜,但是还是请注明出处(我觉得应该没人偷我的博客) https://blog.csdn.net/qq_43346903/article/details/87879125
一眼二分,然后把小于二分值的点的边连上
check就做最小链覆盖就行了
Code:
#include<bits/stdc++.h>
using namespace std;
inline int read(){
int res=0,f=1;char ch=getchar();
while(!isdigit(ch)) {if(ch=='-') f=-f;ch=getchar();}
while(isdigit(ch)) {res=(res<<1)+(res<<3)+(ch^48);ch=getchar();}
return res*f;
}
const int N=550;
int tot=0,use[N],line[N];
int a[N][N],n,m,v[N];
inline void floyd(){
for(int k=1;k<=m;k++)
for(int i=1;i<=m;i++)
for(int j=1;j<=m;j++)
if(a[i][k] && a[k][j]) a[i][j]=1;
}
int pt[N];
int dfs(int v){
for(int i=1;i<=tot;i++)
if(!pt[i] && a[use[v]][use[i]]){
pt[i]=1;
if(!line[i] || dfs(line[i])){
line[i]=v;
return 1;
}
}
return 0;
}
inline int check(int x){
tot=0;
for(int i=1;i<=m;i++) if(v[i]<x) use[++tot]=i;
int ret=tot;
for(int i=1;i<=tot;i++){
memset(pt,0,sizeof(pt));
ret-=dfs(i);
}
return ret;
}
int main(){
int maxn=0;
n=read()+1;m=read();
for(int k,i=1;i<=m;i++){
v[i]=read();k=read();maxn=max(maxn,v[i]);
for(int y,j=1;j<=k;j++) y=read(),a[i][y]=1;
}
floyd();
int l=1,r=maxn,mid;
int ans=0;
while(l<=r){
int mid=(l+r)>>1;
memset(line,0,sizeof(line));
if(check(mid)<=n) ans=mid,l=mid+1;
else r=mid-1;
}
if(ans>=maxn) cout<<"AK"<<endl;
else cout<<ans<<endl;
return 0;
}