FZU-2295 Human life(网络流 | 最大权闭合子图)

题目链接:http://acm.fzu.edu.cn/problem.php?pid=2295

题目大意:Xzz要开始找工作,要做第 i 个工作需要有 mi 个前置技能,做完第 i 个工作可以获得 wi 个金币,同时有 k 对工作是无法同时做的,只能选择其中一个做。如果Xzz想要学习第 i 个技能的话需要花费 vi 个金币,同时要先学完ni个前置技能。现在问Xzz去学习技能和做部分工作最多能获得多少金币。

题目思路:我们可以将工作和技能都看成图上的点,工作所代表的点能获得金币,所以这个点的权值是正的;技能所代表的点需要花费金币,所以这个点的权值是负的。由于每个技能和工作都有前置条件,所以我们可以令前置技能往当前的工作或者技能连一条有向边。现在问题就转化为从图上任意一个入度为0的点(也就是不需要任何前置技能的点),走完整个图能获得的最大权值为多少。(现在先不考虑各个工作之间的限制条件)

根据样例二,我们可以建立如下的图

(n代表技能,m代表工作,后面的数字为技能编号,逗号后为权值)

这个题转化到这一步就是一个很经典的最大权闭合子图问题了。我们先建立一个超级源点S和一个超级汇点T,S向所有权值为正的点连一条流量为权值的边,所有权值为负的点向T连一条流量为权值的边。中间有前置关系的之间连一条流量为INF的边,再求一遍最小割,用所有正权值之和减去最小割的值,就是这个问题的答案了。

上图转化成的图为:

但本题还有一个限制条件,就是有些工作不能同时被选。处理这个条件,我们可以用2进制状压来枚举有限制条件的一对工作选哪一个,再重新建图,对每种情况求一次最小割,最后取最大的答案即可。比如样例2,就是建成如下两个图,分别跑最小割即可。

                                             

具体实现看代码:

#include <cstdio>
#include <cstring>
#include <queue>
#include <iostream>
#include <vector>
#include <algorithm>
#define fi first
#define se second
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define pb push_back
#define MP make_pair
#define lowbit(x) x&-x
#define clr(a) memset(a,0,sizeof(a))
#define _INF(a) memset(a,0x3f,sizeof(a))
#define FIN freopen("in.txt","r",stdin)
#define IOS ios::sync_with_stdio(false)
#define debug(x) cout<<"["<<x<<"]"<<endl
using namespace std;
typedef long long LL;
typedef pair<int, int>pii;
typedef pair<LL, LL>pll;
const LL inf = 0x3f3f3f3f3f3f3f3f;
const int MX = 405;
const int MXE = 4 * MX * MX;
struct MaxFlow {
    struct Edge {
        int v, nxt;
        LL w;
    } edge[MXE];
    int tot, num, s, t;
    int head[MX];
    void init() {
        memset(head, -1, sizeof(head));
        tot = 0;
    }
    void add(int u, int v, LL w) {
        edge[tot].v = v;
        edge[tot].w = w;
        edge[tot].nxt = head[u];
        head[u] = tot++;

        edge[tot].v = u;
        edge[tot].w = 0;
        edge[tot].nxt = head[v];
        head[v] = tot++;
    }
    int  vis[MX];
    LL d[MX], gap[MX];
    void bfs() {
        memset(d, 0, sizeof(d));
        memset(gap, 0, sizeof(gap));
        memset(vis, 0, sizeof(vis));
        queue<int>q;
        q.push(t);
        vis[t] = 1;
        while (!q.empty()) {
            int u = q.front();
            q.pop();
            for (int i = head[u]; ~i; i = edge[i].nxt) {
                int v = edge[i].v;
                if (!vis[v]) {
                    d[v] = d[u] + 1;
                    gap[d[v]]++;
                    q.push(v);
                    vis[v] = 1;
                }
            }
        }
    }
    int last[MX];
    LL dfs(int u, LL f) {
        if (u == t) return f;
        LL sap = 0;
        for (int i = last[u]; ~i; i = edge[i].nxt) {
            int v = edge[i].v;
            if (edge[i].w > 0 && d[u] == d[v] + 1) {
                last[u] = i;
                LL tmp = dfs(v, min(f - sap, edge[i].w));
                edge[i].w -= tmp;
                edge[i ^ 1].w += tmp;
                sap += tmp;
                if (sap == f) return sap;
            }
        }
        if (d[s] >= num) return sap;
        if (!(--gap[d[u]])) d[s] = num;
        ++gap[++d[u]];
        last[u] = head[u];
        return sap;
    }
    LL solve(int st, int ed, int n) {
        LL flow = 0;
        num = n;
        s = st;
        t = ed;
        bfs();
        memcpy(last, head, sizeof(head));
        while (d[s] < num) flow += dfs(s, inf);
        return flow;
    }
} F;
int n, m, k, _;
pii a[6];
bool vis[MX];
struct node {
    int val, n;
    int p[MX];
} v[MX], w[MX];
int main() {
    //FIN;
    for (scanf("%d", &_); _; _--) {
        scanf("%d%d%d", &n, &m, &k);
        for (int i = 1; i <= n; i++) {
            scanf("%d%d", &v[i].val, &v[i].n);
            for (int j = 1; j <= v[i].n; j++) scanf("%d", &v[i].p[j]);
        }
        for (int i = 1; i <= m; i++) {
            scanf("%d%d", &w[i].val, &w[i].n);
            for (int j = 1; j <= w[i].n; j++) scanf("%d", &w[i].p[j]);
        }
        for (int i = 0; i < k; i++)
            scanf("%d%d", &a[i].fi, &a[i].se);
        int st = 0, ed = n + m + 1;
        LL ans = 0;
        for (int sta = 0; sta < (1 << k); sta++) {
            clr(vis);
            for (int j = 0; j < k; j++) {
                if (vis[a[j].fi] == 1 && vis[a[j].se] == 1) continue;
                if ((sta >> j) & 1) vis[a[j].se] = 1;
                else vis[a[j].fi] = 1;
            }
            F.init();
            LL sum = 0;
            for (int i = 1; i <= m; i++) {
                if (vis[i]) continue;
                sum += w[i].val;
                F.add(st, i, w[i].val);
                for (int j = 1; j <= w[i].n; j++)
                    F.add(i, w[i].p[j] + m, inf);
            }
            for (int i = 1; i <= n; i++) {
                F.add(i + m, ed, v[i].val);
                for (int j = 1; j <= v[i].n; j++)
                    F.add(i + m, v[i].p[j] + m, inf);
            }
            LL flow = F.solve(st, ed, n + m + 2);
            ans = max(ans, sum - flow);
        }
        cout << ans << endl;
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/Lee_w_j__/article/details/82083744
今日推荐