图论专题 - 解题报告 - L

首先想,这里有一堆物品,你可以选择取用或者不取,每种有一定的收益,支付一定的代价,那么就决定是你了!背包!(抱歉不是dp,OJ会等你枚举个2n 次?)
权衡奖金与代价,这两者是有我没他的,怎么去处理呢?

建立最大权闭合子图:源点连正点权(即收益),容量为正;负点权(花费)连汇点,容量为负的负(花费较总收益来说原本就是负的,连得时候连正的就行),有关系的实验和器材之间连边容量为正无穷,答案为全部实验收益 - 上面图跑出来的最小割。
解释:正点之间与负点连边为INF最小割跑出来,要么割正边,意义就是舍弃这个方案,要么割负的负边,意义为买器材花钱,无论怎样,要是使得放弃亲戚钱与花的 买电视的钱最少(也就是最小割),答案即为最优。
而花的钱最少,都在割去的边内,我们可以求出最大流,剩下的权值就是我们的最小割。
由于本题与网络流24题中的 太空飞行问题 基本师出同源,这里套用别人题解中一张图来说明:

在这里插入图片描述

最大流求法还是dinic:

#include<bits/stdc++.h>
#define FOR(a, b, c) for(int a=b; a<=c; a++)
#define maxn 100005
#define maxm 55
#define hrdg 1000000007
#define zh 16711680
#define inf 2147483647
#define llinf 9223372036854775807
#define ll long long
#define pi acos(-1.0)
#define ls p<<1
#define rs p<<1|1
#define int long long
using namespace std;

inline int read(){
    char c=getchar();long long x=0,f=1;
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
    return x*f;
}

int n, m, u, v, d, depth[maxn];
int award, num, need, cost, sum = 0;
int ans;
int start, goal;
struct Edge{ int nex, to, dis;}edge[maxn*10];
int head[maxn*10], tot, cur[maxn*10];
void add_egde(int u, int  v, int d){
    edge[tot] = {head[u], v, d}; head[u] = tot++;
    edge[tot] = {head[v], u, 0}; head[v] = tot++;
}

bool bfs(int start, int goal){
    memset (depth, -1, sizeof(depth));
    queue <int> q;
    depth[start] = 0;
    q.push(start);
    while (!q.empty())
    {
        int now = q.front(); q.pop();
        for (int i = head[now]; ~i; i = edge[i].nex)
        {
            int to = edge[i].to;
            if (depth[to] < 0 && edge[i].dis > 0)
            {
                depth[to] = depth[now] + 1;
                q.push(to);
            }
        }
    }
    return depth[goal] > 0;
}

int dfs(int now, int goal, int limit) {
        if(now == goal) return limit;
        for(int & i = cur[now]; ~i; i = edge[i].nex) {
            int to = edge[i].to;
            if(edge[i].dis > 0 && depth[to] > depth[now]) {
                int d = dfs(to, goal, min(limit, edge[i].dis));
                if(d > 0) {
                    edge[i].dis -= d;
                    edge[i ^ 1].dis += d;
                    return d;
                }
            }
        }
        depth[now] = -1; 
        return 0;
}

void init(){
    memset(head, -1, sizeof(head));
    tot = 0;
}

int dinic(int start, int goal){
    int flow = 0, f;
    while (bfs(start, goal))
    {
        for (int i = start; i <= goal; i++)
            cur[i] = head[i];
        while (f = dfs(start, goal, inf))
            flow += f;
    }
    return flow;
}

signed main()
{
    init();
    n = read(); m = read();
    start = 0; goal = n + m + 1;
    for (int i = 1; i <= m; i++)
    {
        cost = read();
        add_egde(i + n, goal, cost);
    }
    for (int i = 1; i <= n; i++)
    {
        award = read(); num = read();
        add_egde(start, i, award);
        sum += award;					//总的正向收益累加
        for (int j = 1; j <= num; j++)
        {
            need = read();
            add_egde(i, need + n, inf);
        }
    }
    ans = sum - dinic(start, goal);				//总权值减去最大流
    printf("%lld", ans);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值