bzoj1143[CTSC2008] 祭祀river
题目链接: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; }