题解 CF103E 【Buying Sets】
提供一种更为巧妙的做法,不需要进行二分图匹配。
题意
有一个大小为
每个子集有一个可正可负的权值,你需要选出一些子集使得这些子集并的大小等于子集个数,且所选子集的权值和最小,可以为空集。
题解
把子集和元素分别看成两类物品,我们可以知道,如果我们选择了一个子集,那么我们必须选择所有属于它的元素。这启发我们往最大权闭合子图的方向转化。
我们先忽略“子集并的大小等于子集个数”这个条件,就是最小权闭合子图的经典模型,把每个子集的权值取反就可以做了。
那么,如何解决这一限制呢?
“任意
我们给每个元素赋上
而由于我们可以选择空集,所以一定存在合法的方案。
实现中有两个细节要注意:
- 最大权闭合子图建模的过程中,必须同时选择的物品间流量为
+\infty 的边的权值必须大于赋上的权值。在代码中,我用lim表示赋上的无穷大权值,用inf表示建图过程中的无穷大权值。 - 由于可以选择空集,输出时答案要和
0 取个\min 。
代码
#include<bits/stdc++.h>
#define reg register
using namespace std;
typedef long long ll;
const int lim=3e8+5;
const int inf=0x3f3f3f3f;
const int V=605;
const int E=181205;
int to[E],nxt[E],c[E],h[V],cnt;
inline void ins(int s,int t,int w){
to[cnt]=t;nxt[cnt]=h[s];c[cnt]=w;h[s]=cnt++;
to[cnt]=s;nxt[cnt]=h[t];c[cnt]=0;h[t]=cnt++;
}
int S,T,level[V],iter[V],que[V];
bool bfs(){
memset(level,-1,sizeof(level));
reg int he=0,ta=1;que[0]=S;level[S]=1;
while(he<ta){
reg int v=que[he++];
for(reg int i=h[v];~i;i=nxt[i]){
if(c[i]&&level[to[i]]<0){
level[to[i]]=level[v]+1;
que[ta++]=to[i];
}
}
}
return level[T]>0;
}
int dfs(int st,int f){
if(st==T)return f;
reg int used=0,w;
for(reg int& i=iter[st];~i;i=nxt[i])
if(c[i]&&level[to[i]]==level[st]+1){
w=dfs(to[i],min(c[i],f-used));
if(!w)continue;
c[i]-=w;c[i^1]+=w;used+=w;
if(f==used)return f;
}
return used;
}
int n;
int main(){
memset(h,-1,sizeof(h));
scanf("%d",&n);T=n+n+1;
for(reg int i=1,k,x;i<=n;i++){
scanf("%d",&k);
while(k--)scanf("%d",&x),ins(i,x+n,inf);
}
reg ll Ans=0,f;
for(reg int i=1,x;i<=n;i++)
scanf("%d",&x),ins(S,i,lim-x),Ans+=lim-x;
for(reg int i=1;i<=n;i++)ins(i+n,T,lim);
while(bfs()){
memcpy(iter,h,sizeof(h));
while(f=dfs(S,inf))Ans-=f;
}
printf("%lld\n",min(-Ans,0ll));
return 0;
}