题意:给你n个技能,m个工作,k个互斥关系。每个技能需要花费,并且学习一个技能可能需要先学会其他技能。完成一个工作需要前置技能,完成一个工作你会获得赏金。有k对工作不能同时完成。问你能获得多少赏金。
思路:因为我们完成一个工作需要一些前置技能,所以我们把工作向他所需要的前置技能建立一条边。每个技能向他的前置技能建一条边。那么我们可以发现。我们如果需要完成一个工作。那么该工作的所有后继边所连接的技能我们都要学会,正好对应最大闭合权子图的性质(所谓闭合子图就是给定一个有向图,从中选择一些点组成一个点集V。对于V中任意一个点,其后续节点都仍然在V中。)。那么我们套用模型。建立源点和工作连一条边,容量为该工作所获得的赏金。建立汇点,每个技能和汇点建边,容量为该技能的花费。其他边容量设为INF即可。
//网络流最大流 POJ 1273
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#define ll long long
#define INF 0x3f3f3f3f
using namespace std;
const int N = 1e5 + 5;
int n, m;
int sp, tp;
struct node{
int v, next;
ll cap;
}edge[N * 2];
int head[N], deg[N], cur[N];
int cnt = 0;
void init()
{
cnt = 0;
memset(head, -1, sizeof(head));
}
void add(int u, int v, int cap)
{
edge[cnt].v = v;
edge[cnt].cap = cap;
edge[cnt].next = head[u];
head[u] = cnt++;
edge[cnt].v = u;
edge[cnt].cap = 0;
edge[cnt].next = head[v];
head[v] = cnt++;
}
int bfs()
{
memset(deg, -1, sizeof(deg));
deg[sp] = 0;
queue<int> q;
q.push(sp);
while(!q.empty())
{
int u = q.front();
if(u == tp) return 1;
q.pop();
for(int i = head[u]; ~i; i = edge[i].next)
{
int v = edge[i].v;
ll cap = edge[i].cap;
if(deg[v] == -1 && cap)
{
deg[v] = deg[u] + 1;
q.push(v);
}
}
}
return 0;
}
ll dfs(int u, ll flow)
{
if(u == tp || flow == 0) return flow;
ll res = 0, f;
for(int &i = cur[u]; ~i; i = edge[i].next)
{
int v = edge[i].v;
ll cap = edge[i].cap;
if(deg[v] == deg[u] + 1 && (f = dfs(v, min(flow - res,cap))) > 0)
{
edge[i].cap -= f;
edge[i ^ 1].cap += f;
res += f;
if(res == flow) return flow;
}
}
if(!res) deg[u] = -1;
return res;
}
ll dinic()
{
ll ans = 0;
while(bfs())
{
memcpy(cur, head, sizeof(head));
ans += dfs(sp, INF);
}
return ans;
}
int a[305];int vis[305];
struct Node{
int per[305];
int num;
int val;int id;
}job[305],sk[305];
int main()
{
int t;scanf("%d",&t);
while(t--)
{
int k;
scanf("%d%d%d",&n,&m,&k);
for(int i=1;i<=n;i++) //技能
{
scanf("%d",&sk[i].val);
int p;scanf("%d",&sk[i].num);
sk[i].id=i;
for(int j=0;j<sk[i].num;j++)
{
int x;scanf("%d",&sk[i].per[j]);
}
}
for(int i=1;i<=m;i++) //工作
{
scanf("%d",&job[i].val);
int p;scanf("%d",&job[i].num);
job[i].id=i+n;
for(int j=0;j<job[i].num;j++)
{
int x;scanf("%d",&job[i].per[j]);
}
}
for(int i=0;i<2*k;i++) //互斥工作
{
scanf("%d",&a[i]);
}
int ans=0;
for(int i=0;i<(1<<(2*k));i++)
{
int f=1;
for(int j=0;j<2*k;j++)
{
if((i&(1<<j))==0)
{
if(j%2==0)
{
if(((i)&(1<<(j+1)))==0)f=0;
}
}
}
if(!f) continue;
init();
memset(vis,0,sizeof(vis));
for(int j=0;j<2*k;j++)
{
if(i&(1<<j))
{
vis[a[j]]=1;//不选的工作
}
}
sp=0,tp=n+m+3;
for(int i=1;i<=n;i++)
{
for(int j=0;j<sk[i].num;j++)
{
add(sk[i].id,sk[i].per[j],INF);
}
add(sk[i].id,tp,sk[i].val);
}
int sum=0;
/*for(int i=1;i<=m;i++)
printf("%d ",vis[i]);
printf("\n");*/
for(int i=1;i<=m;i++)
{
if(vis[i]) continue;
add(sp,job[i].id,job[i].val);
for(int j=0;j<job[i].num;j++)
{
add(job[i].id,job[i].per[j],INF);
}
sum+=job[i].val;
}
sum-=dinic();
ans=max(ans,sum);
}
printf("%d\n",ans);
}
return 0;
}
/*
1
5 2 1
1 0
1 1 1
1 0
1 0
1 1 4
10 2 2 3
8 2 3 5
1 2
*/