[51nod1551]集合交易

来源:互联网 发布:盘古网络的优势是什么 编辑:程序博客网 时间:2024/05/01 18:26

Description

给出n个集合,每个集合有代价,可正可负。
现在让你选出一些集合,使得这些集合的交集大小等于选出集合的数量
保证所有集合中任意k个集合的并集大于等于k
求最小代价
n<=300,ai<=n

Solution

让我们来看看这个猎奇条件有什么用?

hall定理:二分图有完备匹配的充要条件是X中的任意k个点都至少和Y中的k个点相邻

于是这告诉我们把每个集合和数字看做一个点,从集合点向它所包含的数字连边,所得的二分图必定有完备匹配
这样又有什么用呢?
我们可以把每个集合对应的匹配点钦定为它的代表点,这样选择这个集合就相当于选择了这个代表点
现在我们就只需要考虑数字点,要保证选出的代表点数量=选出的点的数量
这样就可以建立一些依赖关系了,对原图跑最大权闭合子图就是答案

Solution

#include <cstdio>#include <cstring>#include <algorithm>#define fo(i,a,b) for(int i=a;i<=b;i++)#define rep(i,a) for(int i=last[a];i;i=next[i])using namespace std;const int N=305,inf=0x7fffffff;int n,cnt,x,S,T,a[N][N];int last[N],next[N*N*2],t[N*N*2],f[N*N*2],l;void add(int x,int y,int z) {    t[++l]=y;f[l]=z;next[l]=last[x];last[x]=l;    t[++l]=x;f[l]=0;next[l]=last[y];last[y]=l;}int to[N];bool bz[N];bool find(int x) {    fo(i,1,a[x][0])        if (!bz[a[x][i]]) {            bz[a[x][i]]=1;            if (!to[a[x][i]]||find(to[a[x][i]])) {                to[a[x][i]]=x;                return 1;            }        }    return 0;}int d[N],dis[N];bool bfs() {    memset(dis,0,sizeof(dis));dis[S]=1;    int i=0,j=1;d[1]=S;    while (i<j)         rep(k,d[++i])             if (f[k]&&!dis[t[k]])                 dis[t[k]]=dis[d[i]]+1,d[++j]=t[k];    return dis[T];}int dinic(int x,int y) {    if (x==T) return y;    int now=0;    rep(i,x)        if (f[i]&&dis[t[i]]==dis[x]+1) {            int k=dinic(t[i],min(y,f[i]));            f[i]-=k;f[i^1]+=k;y-=k;now+=k;            if (!y) break;        }    if (!now) dis[x]=-1;    return now;}int main() {    scanf("%d",&n);S=0;T=n+1;l=1;    fo(i,1,n) {        scanf("%d",&a[i][0]);        fo(j,1,a[i][0]) scanf("%d",&a[i][j]);    }    fo(i,1,n) {        memset(bz,0,sizeof(bz));        find(i);    }    fo(i,1,n)         fo(j,1,a[i][0])             if (to[a[i][j]]!=i)                add(i,to[a[i][j]],inf);    int ans=0;    fo(i,1,n) {        scanf("%d",&x);x=-x;        if (x>0) add(S,i,x),ans+=x;        else add(i,T,-x);    }    while (bfs()) ans-=dinic(S,inf);    ans=-ans;    printf("%d\n",min(ans,0));}
原创粉丝点击