题目链接:bzoj1143
题目大意:
n个点m条有向边(保证无环)。要求选最多的点,使该点集中每个点都不能到达点集中的其他点。
题解:
最长反链
我觉得能想到匈牙利好厉害啊。根本不会往哪方面想。。。
因为有个背景知识...最长反链与最小链覆盖
大概可以看看这个http://vfleaking.blog.163.com/blog/static/1748076342012918105514527/
所以这道题就是裸的求最长反链=最小链覆盖。而求最小链覆盖数的方法就是首先传递闭包,然后就变成了最小路径覆盖。拆点,用二分图匹配解决。
因为n<=100很小,所以直接floyd搞就好了。
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define maxn 110
bool bo[maxn][maxn];
int n,ask[maxn],bf[maxn],tim;
int ffind(int x)
{
for (int i=1;i<=n;i++)
if (bo[x][i] && ask[i]!=tim)
{
ask[i]=tim;
if (bf[i]==-1 || ffind(bf[i]))
{
bf[i]=x;
return true;
}
}
return false;
}
int main()
{
//freopen("a.in","r",stdin);
//freopen("a.out","w",stdout);
int m,i,j,k,x,y,sum,ans;
scanf("%d%d",&n,&m);
memset(bo,false,sizeof(bo));
for (i=1;i<=m;i++)
{
scanf("%d%d",&x,&y);
bo[x][y]=true;
}
for (k=1;k<=n;k++)
for (i=1;i<=n;i++)
for (j=1;j<=n;j++) if (i!=j)
if (i!=k && j!=k)
if (bo[i][k] && bo[k][j]) bo[i][j]=true;
ans=tim=0;
memset(ask,0,sizeof(ask));
memset(bf,-1,sizeof(bf));
for (i=1;i<=n;i++)
{
tim++;
if (ffind(i)) ans++;
}
ans=n-ans;
printf("%d\n",ans);
return 0;
}