【题目分析】
这是一道最小割的应用题,首先从源点向每一个任务连一条容量为能获得的钱的边,然后把每一个需要雇的人向汇点连一条为Vi的边,最后把每一个任务向需要的人连一条容量为INF的边,然后跑一遍最大流,那么割掉(满流)的部分如果在左侧代表我不做这个任务,那么用所有任务的价值和前去最大流(不做的那一部分) ,就是最大的价值了。
因为HOJ崩了,无法提交,所以并没有测试。但是样例过了,应该没有什么问题了。有时间再测吧
【代码】
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <cstring>
using namespace std;
const int INF=1000000;
#define M1(a) memset(a,-1,sizeof a)
#define M0(a) memset(a,0,sizeof a)
#define F(i,j,k) for (int i=j;i<=k;++i)
#define maxn 50010
int sum=0,en=0,tt,u[maxn],v[maxn],h[maxn],ne[maxn],map[maxn],f[maxn],S,T;
inline void add(int a,int b,int r)
{
u[en]=a;v[en]=b;ne[en]=h[a];f[en]=r;h[a]=en++;
u[en]=b;v[en]=a;ne[en]=h[b];f[en]=0;h[b]=en++;
}
inline bool tell()
{
memset(map,-1,sizeof map);
int que[501];
int head=0,tail=0;
que[tail++]=S;
map[S]=0;
while (head<tail){
int u=que[head++];
for (int i=h[u];i!=-1;i=ne[i]){
if (map[v[i]]==-1&&f[i]){
map[v[i]]=map[u]+1;
que[tail++]=v[i];
}
}
}
if (map[T]!=-1) return true;
else return false;
}
inline int zeng (int k,int now)
{
if (k==T) return now;
int r=0;
for (int i=h[k];i!=-1&&now>r;i=ne[i]){
if (map[k]+1==map[v[i]]&&f[i]!=0){
int t=zeng(v[i],min(now-r,f[i]));
f[i]-=t;f[i^1]+=t;r+=t;
}
}
if (!r) map[k]=-1;
return r;
}
inline int dinic()
{
int r=0,t;
while (tell()) while (t=zeng(S,INF)) r+=t;
return r;
}
inline void reset()
{
M1(u);M1(v);M1(h);M1(ne);
}
inline void solve()
{
int n,m;
scanf("%d%d",&n,&m);
S=0;T=n+m+1;
for (int i=1;i<=n;++i){
int x;
scanf("%d",&x);
sum+=x;
add(S,i,x);
}
for (int i=1;i<=m;++i){
int x;
scanf("%d",&x);
add(i+n,T,x);
}
for (int i=1;i<=n;++i){
int x;
scanf("%d",&x);
for (int j=1;j<=x;++j){
int y;
scanf("%d",&y);
add(i,y+1+n,INF);
}
}
cout<<sum-dinic();
}
int main()
{
scanf("%d",&tt);
F(i,1,tt) {reset();solve();}
}