您的位置:首页 > 其它

[51nod1551]集合交易

2017-11-04 21:12 190 查看

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

;
int last
,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
;
bool bz
;
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
,dis
;
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));
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: