问题抽象:给一个DAG,选择尽量多的点使彼此之间不存在祖先-后代关系。
5%:
暴力枚举每一个点是否被选中,时间复杂度:O(2^n)
20%:
在上一个做法的基础上加上一些剪枝。时间复杂度:O(2^n)。
此算法亦可通过n==200的测试点,且只需要16ms(luogu上)。
树的部分分:
容易发现选择全部叶节点即可。时间复杂度:O(n)
“每个会员要么没有上司,要么没有下属”的:
容易发现此时的DAG是一个二分图。使用经典的二分图最大独立集算法(点数-最大匹配数)即可。时间复杂度:O(m*sqrt(n))。
“全是直接下属”的:
我也不知道怎么做,这个部分分只是为了让孙诺舟的错误算法多拿一些分。
100%:
容易发现此题是一道DAG最大独立集裸题(参见 CTSC2008祭祀),而且不用输出方案。(但我真的不是出的原题,纯属巧合)
由于时间原因,在此复制@白苏小公子喵 的题解:
在有向无环图中,我们定义:
链:图上一些点的集合,对于链上任意两个点x、y,满足x能到达y或者y能到达x。
反链:图上一些点的的集合,对于反链上任意两个点x、y,满足x不能到达y并且y不能到达x。
所以就是很显然的求最长反链长度了~
有以下Dilworth定理:
最长反链长度=最小链覆盖(选取最少的链覆盖所有的点)->证明详见最长反链与最小链覆盖。所以就又转化成了求最小路径(链)覆盖了,来看怎么求:
选择建一个二分图,两边各有n个点,原来的点node分别对应两个图中的node1、node2。如果原图中存在边 x->y,那么就在二分图上建立边 x1->y2。
跑一遍匈牙利,则有 原图最小路径覆盖=原点数n-二分图最大匹配
(当然也可以用网络流,则有 原图最小路径覆盖=原点数n-最大流)
为什么呢?考虑每在二分图上连一条边,就相当于将两条路径连成一条,那么最小链覆盖数就减少了1(少用一条链覆盖所有点了)。我们将一个点拆成两个,跑二分图最大匹配,避免了路径相交的问题,保证所选出来的每一条一定为一条链。
#include
#define maxn 10010
#define int64 long long
#define FO(x) freopen(#x".in","r",stdin),freopen(#x".out","w",stdout)
using namespace std;
bitset isfa[maxn];
int n,m,que[maxn],cnt;
int ind[maxn],top[maxn];
vector<int> v[maxn],vb[maxn];
struct edge{
int u,v,cap;
};
struct Dinic{
int n,s,t,dis[maxn],cur[maxn],que[maxn];
vector e;vector<int>v[maxn];
void Init(int n){
this->n=n;e.clear();
for(int i=0;ivoid AddEdge(int x,int y,int flw){
e.push_back((edge){x,y,flw});
e.push_back((edge){y,x,0});
v[x].push_back(e.size()-2);
v[y].push_back(e.size()-1);
}
int bfs(){
memset(dis,0x3f,sizeof dis);
int l=1,r=1;que[1]=s;dis[s]=0;
while(l<=r){
int p=que[l++],to,i;
for(int t=0;t<(int)v[p].size();++t)if(e[i=v[p][t]].cap && dis[to=e[i].v]>1e9)
dis[to]=dis[p]+1,que[++r]=to;
}
return dis[t]<1e9;
}
int dfs(int p,int a){
if(p==t || !a)return a;
int sf=0,flw;
for(int &i=cur[p],to;i<(int)v[p].size();++i){
edge &E=e[v[p][i]];
if(dis[to=E.v]==dis[p]+1 && (flw=dfs(to,min(a,E.cap)))){
E.cap-=flw;e[v[p][i]^1].cap+=flw;
a-=flw;sf+=flw;
if(!a)break;
}
}
return sf;
}
int dinic(int s,int t){
this->s=s;this->t=t;
int flw=0;
while(bfs()){
memset(cur,0,sizeof cur);
flw+=dfs(s,1e9);
}
return flw;
}
}sol;
int main(){
FO(dance);
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++){
int x,y;scanf("%d%d",&x,&y);
v[x].push_back(y);ind[y]++;
vb[y].push_back(x);
}
int l=1,r=0;
for(int i=1;i<=n;i++)if(!ind[i])que[++r]=i;
while(l<=r){
int now=que[l++];top[++cnt]=now;
for(int i=0;i<(int)v[now].size();i++)
if(!--ind[v[now][i]])que[++r]=v[now][i];
}
for(int i=1;i<=n;i++){
int p=top[i];isfa[p][p]=1;
sol.AddEdge(0,i*2,1);
sol.AddEdge(i*2+1,1,1);
for(int j=0;j<(int)vb[p].size();j++)
isfa[p]|=isfa[vb[p][j]];
for(int j=1;j<=n;j++)if(isfa[p][j] && p!=j)
sol.AddEdge(j*2,p*2+1,1);
}
printf("%d\n",n-sol.dinic(0,1));
return 0;
}