51nod1551 集合交易

来源:互联网 发布:网络语言暴力案例 编辑:程序博客网 时间:2024/05/01 07:48

把集合和元素建二分图连边,根据Hall定理,此图存在完美匹配。求出一组完美匹配。
接下来考虑怎样的选法是合法的。可以发现,如果选择了k个集合,那么我们至少选择了匹配边连到的k个元素。也就是说,不能再有某个集合的非匹配边连到一个匹配边没有被选择的物品。再换句话说,如果选择了一个集合,这个集合所有非匹配边连到物品的匹配边连到的集合是必选的。容易发现这也是充分条件,因此问题转化成了依赖关系,用最大权闭合子图求解。

#include<cstdio>#include<vector>#include<algorithm>using namespace std;const int maxn=200000,oo=0x3f3f3f3f;vector<int> e[maxn],a[maxn];int w[maxn],to[maxn],val[maxn],f[maxn],g[maxn],dep[maxn],que[maxn],n,num,s,t;int dfs(int u,int lim){    int ret=0,v,x;    if (u==t) return lim;    for (vector<int>::iterator it=e[u].begin();it!=e[u].end()&&ret<lim;++it)        if (w[*it]&&dep[v=to[*it]]==dep[u]+1)        {            x=dfs(v,min(lim-ret,w[*it]));            ret+=x;            w[*it]-=x;            w[*it^1]+=x;        }    if (ret<lim) dep[u]=0;    return ret;}int bfs(){    int hd=1,tl=1,u,v;    for (int i=0;i<=t;i++) dep[i]=0;    que[1]=s;    dep[s]=1;    while (hd<=tl)    {        u=que[hd++];        for (vector<int>::iterator it=e[u].begin();it!=e[u].end();++it)            if (w[*it]&&!dep[v=to[*it]])            {                dep[v]=dep[u]+1;                que[++tl]=v;            }    }    return dep[t];}void add(int u,int v,int x){    num++;    e[u].push_back(num*2);    w[num*2]=x;    to[num*2]=v;    e[v].push_back(num*2+1);    w[num*2+1]=0;    to[num*2+1]=u;}int main(){    //freopen("c.in","r",stdin);    int x,y,ans=0;    scanf("%d",&n);    for (int i=1;i<=n;i++)    {        scanf("%d",&x);        while (x--)        {            scanf("%d",&y);            a[i].push_back(y);            add(i,y+n,1);        }    }    for (int i=1;i<=n;i++) scanf("%d",&val[i]);    s=2*n+1;    t=2*n+2;    for (int i=1;i<=n;i++)    {        add(s,i,1);        add(i+n,t,1);    }    while (bfs()) dfs(s,oo);    for (int i=1;i<=n;i++)        for (vector<int>::iterator it=e[i].begin();;++it)            if (!w[*it]&&to[*it]!=s)            {                f[i]=to[*it]-n;                g[to[*it]-n]=i;                break;            }    for (int i=0;i<=t;i++) e[i].clear();    num=0;    for (int i=1;i<=n;i++)    {        if (val[i]>0) add(i,t,val[i]);        else        {            ans+=val[i];            add(s,i,-val[i]);        }        for (vector<int>::iterator it=a[i].begin();it!=a[i].end();++it)            if (*it!=f[i]) add(i,g[*it],oo);    }    while (bfs()) ans+=dfs(s,oo);    printf("%d\n",ans);}
原创粉丝点击