【BZOJ 1143】[CTSC2008]祭祀river
【链接】 我是链接,点我呀:)
【题意】
【题解】
这道题要求的是一个最长反链长度 (点集中任意两点之间都不能到达,且点的个数最多)最长反链长度=最小链覆盖(也即最小路径覆盖)
最小路径覆盖可以用以下方法求得。
首先,把每个点都x分成两个点x1,x2
(x1放在左边,x2都放在右边,这样左右两边就都有n个点了
如果有一条边从x指向y。
那么就从左边的x1连一条边到右边的y2
然后在这样的规则下形成的二分图上。
我们做一下二分图匹配的最大匹配。
这样的结果就是路径不相交的最短路径覆盖了。
要怎么理解?
可以这样想。
一开始每个点单独成一条路径,ans=n条路径。
然后我们在做匹配的时候。
就是把两条路径合在一起。
会发现在这样的图上做匹配。就是把一条一条路径不相交的连起来。
每次做一下匹配,ans--
因为必然有条边长度更长的路径生成了。
(然后两条路径合在一起了,注意右边的点仍然表示那个点,不是虚构的,左边的点也是一样
(这样用匹配的定义,巧妙的把路径都串联起来了
但是这样我们做出来的是一个没有路径相交的覆盖。
最小路径覆盖是允许路径相交的。
那么我们在从左边往右边连边的时候。
先做一个floyd,求出两两之间的连通性。
这样,中间就能够之间跳过某些路径。
比如
1 5
\ /
3 - 4
/ \
2 6
这样的图
1和5因为可以通达,所以直接有边相连
而2-3-4-6也是相连的
因此,此时我们再套用上面不允许路径覆盖的方法,建立二分图
就能够找到1-3-4-5和2-3-4-6这两条有覆盖的路径了
【代码】
#include <bits/stdc++.h>
#define LL long long
#define rep1(i,a,b) for (int i = a;i <= b;i++)
#define rep2(i,a,b) for (int i = a;i >= b;i--)
#define all(x) x.begin(),x.end()
#define pb push_back
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
using namespace std;
const double pi = acos(-1);
const int dx[4] = {0,0,1,-1};
const int dy[4] = {1,-1,0,0};
const int N = 100;
int n,m,g[N+10][N+10],G[N+10][N+10],pre[N+10];
bool flag[N+10];
bool dfs(int x){
rep1(i,1,n)
if (flag[i]==false && G[x][i]){
flag[i] = true;
if (pre[i]==0 || dfs(pre[i])){
pre[i] = x;
return true;
}
}
return false;
}
int main(){
#ifdef LOCAL_DEFINE
freopen("rush_in.txt", "r", stdin);
#endif
scanf("%d%d",&n,&m);
rep1(i,1,m){
int x,y;
scanf("%d%d",&x,&y);
g[x][y] = 1;
}
rep1(k,1,n)
rep1(i,1,n)
if (i!=k)
rep1(j,1,n)
if (j!=k && j!=i){
g[i][j] |= (g[i][k] & g[k][j]);
}
rep1(i,1,n)
rep1(j,1,n)
if (i!=j && g[i][j]){
G[i][j] = 1;
}
int ans = n;
rep1(i,1,n){
memset(flag,0,sizeof flag);
if (dfs(i)) ans--;
}
printf("%d\n",ans);
return 0;
}