题目大意
给出一幅有向图,选取一些点,这些点不能从一个点到达另一个点,求最多能选多少个点。
题解
其实我们用floyd传递闭包,知道哪些点不能同时取。显然就是求最大独立集,对偶问题是最小点覆盖,等价于二分图最大匹配,建个图后跑一遍网络流就可以了。
最大独立集对偶问题是最小点覆盖
设属于独立集与属于覆盖集的共同构成整个图,由覆盖集的定义,一条边至少有一个点属于覆盖集,也就是没有一条边两个点都属于独立集,所以得证属于独立集与属于覆盖集的共同构成整个图。所以当覆盖集最小时,独立集最大。
最小点覆盖等价于二分图最大匹配
割边代表选取了某一个点并付出代价,最小割使得一条边至少一个点被选中,满足了覆盖集的要求,而最小割就最小化选的点,即最小点覆盖。
ps:以上证明比较感性
code
using namespace std;
int const maxn=200,maxm=30000,inf=2147483647;
int n,m,cntedge=2,begin[maxn*2+10],to[maxn*maxn*2+maxn*4+10],size[maxn*maxn*2+maxn*4+10],
cost[maxn*maxn*2+maxn*4+10],next[maxn*maxn*2+maxn*4+10],dis[maxn*2+10];
bool done[maxn*2+10],tong[maxn+10][maxn+10];
void insert(int u,int v,int w,int c){
to[cntedge]=v;
size[cntedge]=w;
cost[cntedge]=c;
next[cntedge]=begin[u];
begin[u]=cntedge++;
to[cntedge]=u;
size[cntedge]=0;
cost[cntedge]=-c;
next[cntedge]=begin[v];
begin[v]=cntedge++;
}
int addflow(int now,int t,int flow){
done[now]=1;
if(now==t)return flow;
for(int i=begin[now];i;i=next[i])
if(size[i]&&(!done[to[i]])&&(dis[now]==dis[to[i]]+cost[i])){
int add=addflow(to[i],t,min(flow,size[i]));
if(add){
size[i]-=add;size[i^1]+=add;
return add;
}
}
return 0;
}
bool updis(){
int add=inf;
fo(i,0,n*2+1)
if(done[i])
for(int j=begin[i];j;j=next[j])
if(size[j]&&(!done[to[j]]))
add=min(add,dis[to[j]]+cost[j]-dis[i]);
if(add==inf)return 0;
fo(i,0,n*2+1)
if(done[i])dis[i]+=add;
return 1;
}
int main(){
freopen("d.in","r",stdin);
freopen("d.out","w",stdout);
scanf("%d%d",&n,&m);
fo(i,1,m){
int u,v;scanf("%d%d",&u,&v);
tong[u][v]=1;
}
fo(k,1,n)
fo(i,1,n)
fo(j,1,n)
tong[i][j]|=tong[i][k]&&tong[k][j];
fo(i,1,n)
fo(j,1,n)
if(tong[i][j])insert(i,n+j,1,1);
fo(i,1,n)insert(0,i,1,1),insert(n+i,n*2+1,1,1);
int ans=0;
do{
memset(done,0,sizeof(done));int add;
while(add=addflow(0,n*2+1,inf)){
ans+=add;
memset(done,0,sizeof(done));
}
}while(updis());
printf("%d",n-ans);
return 0;
}