没有上司的舞会

问题抽象:给一个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<bits/stdc++.h>
#define maxn 10010
#define int64 long long
#define FO(x) freopen(#x".in","r",stdin),freopen(#x".out","w",stdout)
using namespace std;
bitset<maxn> 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<edge>e;vector<int>v[maxn];
    void Init(int n){
        this->n=n;e.clear();
        for(int i=0;i<n;i++)v[i].clear();
    }
    void 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;
}

猜你喜欢

转载自blog.csdn.net/xyc1719/article/details/81747103
今日推荐