FZU - 2295 (最大闭合权子图)

题意:给你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
*/
发布了155 篇原创文章 · 获赞 32 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/qq_37632935/article/details/89398349
今日推荐