拍照
超级源点向每个集合连权值为这个集合的值的边,每个集合向每个元素连权值为极大值的边,每个元素向超级汇点连权值为其花费的边。
答案即为集合值的和减去最大流(最小割)
看代码:
#include<bits/stdc++.h> using namespace std; #define int long long #define inf 1e12 const int maxn=100005; int n,m; int beg[maxn],nex[maxn],to[maxn],w[maxn],e; inline void add(int x,int y,int z){ nex[e]=beg[x];beg[x]=e; to[e]=y;w[e]=z;e++; } int dep[maxn]; queue<int>q; int bfs(){ //puts("bfs"); memset(dep,0,sizeof(dep)); while(!q.empty())q.pop(); dep[0]=1; q.push(0); while(!q.empty()){ int x=q.front(); q.pop(); for(int i=beg[x];~i;i=nex[i]){ int t=to[i]; if(w[i]&&dep[t]==0){ dep[t]=dep[x]+1; q.push(t); if(t==n+m+1)return 1; } } } return 0; } int dfs(int x,int lim){ if(!lim||x==n+m+1)return lim; int ans=0; for(int i=beg[x];~i;i=nex[i]){ int t=to[i]; if(dep[t]==dep[x]+1){ int f=dfs(t,min(w[i],lim)); ans+=f;w[i^1]+=f; w[i]-=f;lim-=f; } } return ans; } signed main(){ memset(beg,-1,sizeof(beg)); cin>>m>>n; int val,x,ans=0; for(int i=1;i<=m;i++){ scanf("%lld",&val); ans+=val; add(0,n+i,val); add(n+i,0,0); while(1){ scanf("%lld",&x); if(!x)break; add(n+i,x,inf); add(x,n+i,0); } } for(int i=1;i<=n;i++){ scanf("%lld",&val); add(i,n+m+1,val); add(n+m+1,i,0); } while(bfs())ans-=dfs(0,inf); printf("%lld\n",ans); return 0; }
深深地感到自己的弱小。