卧薪尝胆,厚积薄发。
集合交易
Date: Mon Aug 27 07:37:49 CST 2018 In Category: NoCategory

Description:

有一些集合在卖,价钱可正可负,集合满足 $\forall\ k\in[1,n]$ , $k$ 个任意集合的并集元素的个数不会小于 $k$ 个,要求买到的集合的个数等于这些集合的并集中元素的个数,在满足这个前提下花费尽量少,可以一个都不买。
$1\le n \le 300,1\le a_i\le n$

Solution:

把集合看成左部点,元素看成右部点,那么这一定是个二分图,条件的含义即为从左边选出 $k$ 个点,右边一定有至少 $k$ 个点与这些点相连,这其实就是霍尔定理的条件,根据霍尔定理,该图一定有完备匹配,又由 $a_i\le n$ ,左右一定可以找出一个完备匹配使他们两两对应,不妨先用匈牙利算法把这个匹配求出来,然后用这个元素作为这个集合的代表元,那么如果选了这个集合,就必须选这个集合除代表元之外的其它元素所代表的集合,可以根据这个限制建立最大权闭合子图的模型,可以发现这样最后求出来的一定是一个集合个数等于元素个数的图,因为每个集合的代表元素都被选了,每个元素代表的集合都被选了。

Code:


#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<queue>
#include<cstring>
using namespace std;
int n;
#define MAXN 310
#define INF 0x3f3f3f3f
struct NetworkFlow
{
struct edge
{
int to,nxt,f;
}e[MAXN * MAXN * 2 + MAXN * 2 + MAXN * 2];
int edgenum;
int lin[MAXN * 2 + 2];
NetworkFlow(){edgenum = 0;memset(lin,-1,sizeof(lin));}
void add(int a,int b,int f)
{
e[edgenum].to = b;e[edgenum].f = f;e[edgenum].nxt = lin[a];lin[a] = edgenum;++edgenum;
e[edgenum].to = a;e[edgenum].f = 0;e[edgenum].nxt = lin[b];lin[b] = edgenum;++edgenum;
return;
}
int s,t;
int ch[MAXN * 2 + 2];
int flow(int k,int f)
{
if(k == t)return f;
int r = 0;
for(int i = lin[k];i != -1 && f > r;i = e[i].nxt)
{
if(ch[e[i].to] == ch[k] + 1 && e[i].f != 0)
{
int l = flow(e[i].to,min(e[i].f,f - r));
e[i].f -= l;r += l;e[i ^ 1].f += l;
}
}
if(r == 0)ch[k] = -1;
return r;
}
bool bfs()
{
memset(ch,-1,sizeof(ch));
queue<int> q;
q.push(s);
ch[s] = 0;
while(!q.empty())
{
int k = q.front();q.pop();
for(int i = lin[k];i != -1;i = e[i].nxt)
{
if(ch[e[i].to] == -1 && e[i].f != 0)
{
ch[e[i].to] = ch[k] + 1;
q.push(e[i].to);
}
}
}
return (ch[t] != -1);
}
int dinic()
{
int ans = 0,r;
while(bfs())while(r = flow(s,INF))ans += r;
return ans;
}
};
int match[MAXN];
bool v[MAXN];
vector<int> p[MAXN];
int g[MAXN][MAXN];
bool find(int k)
{
for(int i = 1;i <= n;++i)
{
if(g[k][i] == 0)continue;
if(!v[i])
{
v[i] = true;
if(match[i] == -1 || find(match[i]))
{
match[i] = k;
return true;
}
}
}
return false;
}
int tr[MAXN];
int main()
{
scanf("%d",&n);
int s,k;
NetworkFlow b;
memset(g,0,sizeof(g));
for(int i = 1;i <= n;++i)
{
scanf("%d",&s);
for(int j = 1;j <= s;++j)
{
scanf("%d",&k);
p[i].push_back(k);
g[i][k] = 1;
}
}
memset(match,-1,sizeof(match));
for(int i = 1;i <= n;++i)
{
memset(v,false,sizeof(v));
find(i);
}
for(int i = 1;i <= n;++i)tr[match[i]] = i;
b.s = n + 1;b.t = b.s + 1;
for(int i = 1;i <= n;++i)
for(vector<int>::iterator it = p[i].begin();it != p[i].end();++it)
if(tr[i] != *it)
b.add(i,match[*it],INF);
int sum = 0;
for(int i = 1;i <= n;++i)
{
scanf("%d",&s);
s = -s;
if(s > 0)sum += s;
if(s > 0)b.add(b.s,i,s);
else b.add(i,b.t,-s);
}
sum -= b.dinic();
cout << min(-sum,0) << endl;
return 0;
}
Copyright © 2020 wjh15101051
ღゝ◡╹)ノ♡